@poppinss/utils 5.0.0 → 6.0.0-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 +661 -873
- package/build/index.d.ts +16 -10
- package/build/index.js +21 -53
- package/build/lodash/main.cjs +37 -0
- package/build/src/{Helpers/base64.d.ts → base64.d.ts} +7 -23
- package/build/src/{Helpers/base64.js → base64.js} +9 -22
- package/build/src/{Helpers/compose.d.ts → compose.d.ts} +0 -11
- package/build/src/compose.js +3 -0
- package/build/src/{defineStaticProperty.d.ts → define_static_property.d.ts} +2 -6
- package/build/src/define_static_property.js +21 -0
- package/build/src/exception.d.ts +16 -0
- package/build/src/exception.js +29 -0
- package/build/src/flatten.d.ts +1 -1
- package/build/src/flatten.js +4 -13
- package/build/src/fs_import_all.d.ts +3 -0
- package/build/src/fs_import_all.js +28 -0
- package/build/src/fs_read_all.d.ts +3 -0
- package/build/src/fs_read_all.js +55 -0
- package/build/src/is_script_file.d.ts +1 -0
- package/build/src/is_script_file.js +12 -0
- package/build/src/json/main.d.ts +7 -0
- package/build/src/json/main.js +6 -0
- package/build/src/json/safe_parse.d.ts +2 -0
- package/build/src/json/safe_parse.js +7 -0
- package/build/src/json/safe_stringify.d.ts +2 -0
- package/build/src/json/safe_stringify.js +18 -0
- package/build/src/message_builder.d.ts +5 -0
- package/build/src/message_builder.js +43 -0
- package/build/src/natural_sort.d.ts +1 -0
- package/build/src/natural_sort.js +3 -0
- package/build/src/object_builder.d.ts +10 -0
- package/build/src/object_builder.js +31 -0
- package/build/src/{Helpers/safeEqual.d.ts → safe_equal.d.ts} +1 -4
- package/build/src/safe_equal.js +14 -0
- package/build/src/slash.d.ts +1 -5
- package/build/src/slash.js +1 -24
- package/build/src/string/bytes.d.ts +6 -0
- package/build/src/string/bytes.js +12 -0
- package/build/src/string/change_case.d.ts +13 -0
- package/build/src/string/change_case.js +75 -0
- package/build/src/string/excerpt.d.ts +4 -0
- package/build/src/string/excerpt.js +10 -0
- package/build/src/string/interpolate.d.ts +1 -0
- package/build/src/{Helpers → string}/interpolate.js +1 -22
- package/build/src/string/main.d.ts +46 -0
- package/build/src/string/main.js +42 -0
- package/build/src/string/milliseconds.d.ts +5 -0
- package/build/src/string/milliseconds.js +16 -0
- package/build/src/string/ordinal.d.ts +1 -0
- package/build/src/string/ordinal.js +21 -0
- package/build/src/string/pluralize.d.ts +12 -0
- package/build/src/string/pluralize.js +12 -0
- package/build/src/string/random.d.ts +1 -0
- package/build/src/string/random.js +7 -0
- package/build/src/string/seconds.d.ts +5 -0
- package/build/src/string/seconds.js +16 -0
- package/build/src/string/slugify.d.ts +2 -0
- package/build/src/string/slugify.js +2 -0
- package/build/src/string/to_sentence.d.ts +5 -0
- package/build/src/string/to_sentence.js +13 -0
- package/build/src/string/truncate.d.ts +4 -0
- package/build/src/string/truncate.js +10 -0
- package/build/src/types.d.ts +14 -0
- package/build/src/types.js +1 -0
- package/lodash/lodash.types.d.ts +44 -0
- package/package.json +70 -82
- package/build/helpers.d.ts +0 -1
- package/build/helpers.js +0 -25
- package/build/src/Exception.d.ts +0 -17
- package/build/src/Exception.js +0 -68
- package/build/src/Helpers/MessageBuilder.d.ts +0 -28
- package/build/src/Helpers/MessageBuilder.js +0 -97
- package/build/src/Helpers/ObjectBuilder.d.ts +0 -46
- package/build/src/Helpers/ObjectBuilder.js +0 -75
- package/build/src/Helpers/compose.js +0 -15
- package/build/src/Helpers/fsReadAll.d.ts +0 -4
- package/build/src/Helpers/fsReadAll.js +0 -23
- package/build/src/Helpers/index.d.ts +0 -14
- package/build/src/Helpers/index.js +0 -62
- package/build/src/Helpers/interpolate.d.ts +0 -7
- package/build/src/Helpers/requireAll.d.ts +0 -8
- package/build/src/Helpers/requireAll.js +0 -65
- package/build/src/Helpers/resolveDir.d.ts +0 -5
- package/build/src/Helpers/resolveDir.js +0 -46
- package/build/src/Helpers/resolveFrom.d.ts +0 -5
- package/build/src/Helpers/resolveFrom.js +0 -27
- package/build/src/Helpers/safeEqual.js +0 -42
- package/build/src/Helpers/string.d.ts +0 -124
- package/build/src/Helpers/string.js +0 -328
- package/build/src/Helpers/types.d.ts +0 -86
- package/build/src/Helpers/types.js +0 -161
- package/build/src/ManagerConfigValidator.d.ts +0 -20
- package/build/src/ManagerConfigValidator.js +0 -55
- package/build/src/defineStaticProperty.js +0 -57
- package/build/src/esmRequire.d.ts +0 -5
- package/build/src/esmRequire.js +0 -20
- package/build/src/esmResolver.d.ts +0 -1
- package/build/src/esmResolver.js +0 -15
- package/build/src/fast-safe-stringify.d.ts +0 -2
- package/build/src/fast-safe-stringify.js +0 -101
- package/build/src/isScriptFile.d.ts +0 -5
- package/build/src/isScriptFile.js +0 -28
- package/build/src/lodash.d.ts +0 -13
- package/build/src/lodash.js +0 -39
- package/build/src/safeParse.d.ts +0 -5
- package/build/src/safeParse.js +0 -42
- package/build/src/safeStringify.d.ts +0 -5
- package/build/src/safeStringify.js +0 -39
- package/types/lodash.types.d.ts +0 -65
package/README.md
CHANGED
|
@@ -1,1264 +1,1052 @@
|
|
|
1
|
-
|
|
1
|
+
# @poppinss/utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A toolkit of utilities used across all the AdonisJS, Edge, and Japa packages
|
|
4
4
|
|
|
5
|
-
> Collection of reusable scripts used by AdonisJS core team
|
|
6
5
|
|
|
7
6
|
[![gh-workflow-image]][gh-workflow-url] [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url] [![synk-image]][synk-url]
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<details>
|
|
12
|
-
<summary>
|
|
13
|
-
<strong>Version 3.0 breaking changes</strong>
|
|
14
|
-
</summary>
|
|
15
|
-
|
|
16
|
-
The version 3.0 re-format the exports to expose an "helpers" subpath to be used within the AdonisJS apps as well.
|
|
17
|
-
|
|
18
|
-
The idea is to separate helpers that we need to share with AdonisJS core inside its own module, accessible as `@poppinss/utils/build/helpers`.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
### Inside helpers subpath
|
|
22
|
-
|
|
23
|
-
Following modules are now moved to a subpath.
|
|
24
|
-
|
|
25
|
-
- `MessageBuilder`
|
|
26
|
-
- `base64`
|
|
27
|
-
- `compose`
|
|
28
|
-
- `fsReadAll`
|
|
29
|
-
- `interpolate`
|
|
30
|
-
- `requireAll`
|
|
31
|
-
- `resolveDir`
|
|
32
|
-
- `resolveFrom`
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
// Earlier
|
|
36
|
-
import {
|
|
37
|
-
MessageBuilder,
|
|
38
|
-
base64,
|
|
39
|
-
compose,
|
|
40
|
-
fsReadAll,
|
|
41
|
-
interpolate,
|
|
42
|
-
requireAll,
|
|
43
|
-
resolveDir,
|
|
44
|
-
resolveFrom,
|
|
45
|
-
safeEqual
|
|
46
|
-
} from '@poppinss/utils'
|
|
47
|
-
|
|
48
|
-
// After version 3.0
|
|
49
|
-
import {
|
|
50
|
-
MessageBuilder,
|
|
51
|
-
base64,
|
|
52
|
-
compose,
|
|
53
|
-
fsReadAll,
|
|
54
|
-
interpolate,
|
|
55
|
-
requireAll,
|
|
56
|
-
resolveDir,
|
|
57
|
-
resolveFrom,
|
|
58
|
-
safeEqual
|
|
59
|
-
} from '@poppinss/utils/build/helpers'
|
|
60
|
-
|
|
61
|
-
````
|
|
62
|
-
|
|
63
|
-
### randomString
|
|
64
|
-
|
|
65
|
-
The `randomString` is now part of the `string` helpers.
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
// Earlier
|
|
69
|
-
import { randomString } from '@poppinss/utils'
|
|
70
|
-
randomString(32)
|
|
71
|
-
|
|
72
|
-
// After version 3.0
|
|
73
|
-
import { string } from '@poppinss/utils/build/helpers'
|
|
74
|
-
string.generateRandom(32)
|
|
75
|
-
````
|
|
76
|
-
|
|
77
|
-
### lodash
|
|
78
|
-
|
|
79
|
-
The following lodash functions have been removed with new alternatives.
|
|
80
|
-
|
|
81
|
-
- `snakeCase`
|
|
82
|
-
- `camelCase`
|
|
83
|
-
- `startCase`
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
// Earlier
|
|
87
|
-
import { lodash } from '@poppinss/utils'
|
|
88
|
-
|
|
89
|
-
lodash.snakeCase()
|
|
90
|
-
lodash.camelCase()
|
|
91
|
-
lodash.startCase()
|
|
92
|
-
|
|
93
|
-
// After version 3.0
|
|
94
|
-
import { string } from '@poppinss/utils/build/helpers'
|
|
95
|
-
|
|
96
|
-
string.snakeCase()
|
|
97
|
-
string.camelCase()
|
|
98
|
-
string.titleCase()
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
</details>
|
|
102
|
-
|
|
103
|
-
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
104
|
-
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
105
|
-
## Table of contents
|
|
106
|
-
|
|
107
|
-
- [Installation](#installation)
|
|
108
|
-
- [Exception](#exception)
|
|
109
|
-
- [esmRequire](#esmrequire)
|
|
110
|
-
- [esmResolver](#esmresolver)
|
|
111
|
-
- [Lodash utilities](#lodash-utilities)
|
|
112
|
-
- [Exported methods](#exported-methods)
|
|
113
|
-
- [Safe stringify](#safe-stringify)
|
|
114
|
-
- [Safe parse](#safe-parse)
|
|
115
|
-
- [defineStaticProperty](#definestaticproperty)
|
|
116
|
-
- [flatten](#flatten)
|
|
117
|
-
- [Helpers](#helpers)
|
|
118
|
-
- [fsReadAll](#fsreadall)
|
|
119
|
-
- [requireAll](#requireall)
|
|
120
|
-
- [resolveFrom](#resolvefrom)
|
|
121
|
-
- [resolveDir](#resolvedir)
|
|
122
|
-
- [interpolate](#interpolate)
|
|
123
|
-
- [Base 64 Encode/Decode](#base-64-encodedecode)
|
|
124
|
-
- [encode](#encode)
|
|
125
|
-
- [decode](#decode)
|
|
126
|
-
- [urlEncode](#urlencode)
|
|
127
|
-
- [urlDecode](#urldecode)
|
|
128
|
-
- [Safe equal](#safe-equal)
|
|
129
|
-
- [Message Builder](#message-builder)
|
|
130
|
-
- [compose](#compose)
|
|
131
|
-
- [Mixins gotchas](#mixins-gotchas)
|
|
132
|
-
- [string](#string)
|
|
133
|
-
- [camelCase](#camelcase)
|
|
134
|
-
- [snakeCase](#snakecase)
|
|
135
|
-
- [dashCase](#dashcase)
|
|
136
|
-
- [pascalCase](#pascalcase)
|
|
137
|
-
- [capitalCase](#capitalcase)
|
|
138
|
-
- [sentenceCase](#sentencecase)
|
|
139
|
-
- [dotCase](#dotcase)
|
|
140
|
-
- [noCase](#nocase)
|
|
141
|
-
- [titleCase](#titlecase)
|
|
142
|
-
- [pluralize](#pluralize)
|
|
143
|
-
- [truncate](#truncate)
|
|
144
|
-
- [excerpt](#excerpt)
|
|
145
|
-
- [condenseWhitespace](#condensewhitespace)
|
|
146
|
-
- [escapeHTML](#escapehtml)
|
|
147
|
-
- [encodeSymbols](#encodesymbols)
|
|
148
|
-
- [toSentence](#tosentence)
|
|
149
|
-
- [prettyBytes](#prettybytes)
|
|
150
|
-
- [toBytes](#tobytes)
|
|
151
|
-
- [prettyMs](#prettyms)
|
|
152
|
-
- [toMs](#toms)
|
|
153
|
-
- [ordinalize](#ordinalize)
|
|
154
|
-
- [generateRandom](#generaterandom)
|
|
155
|
-
- [isEmpty](#isempty)
|
|
156
|
-
- [Types](#types)
|
|
157
|
-
- [lookup](#lookup)
|
|
158
|
-
- [isNull](#isnull)
|
|
159
|
-
- [isBoolean](#isboolean)
|
|
160
|
-
- [isBuffer](#isbuffer)
|
|
161
|
-
- [isNumber](#isnumber)
|
|
162
|
-
- [isString](#isstring)
|
|
163
|
-
- [isArguments](#isarguments)
|
|
164
|
-
- [isObject](#isobject)
|
|
165
|
-
- [isDate](#isdate)
|
|
166
|
-
- [isArray](#isarray)
|
|
167
|
-
- [isRegexp](#isregexp)
|
|
168
|
-
- [isError](#iserror)
|
|
169
|
-
- [isFunction](#isfunction)
|
|
170
|
-
- [isClass](#isclass)
|
|
171
|
-
- [isInteger](#isinteger)
|
|
172
|
-
- [isFloat](#isfloat)
|
|
173
|
-
- [isDecimal](#isdecimal)
|
|
174
|
-
- [ObjectBuilder](#objectbuilder)
|
|
175
|
-
|
|
176
|
-
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
8
|
+
## Why this package exists?
|
|
177
9
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
Install the package from npm registry as follows:
|
|
181
|
-
|
|
182
|
-
```sh
|
|
183
|
-
npm i @poppinss/utils
|
|
184
|
-
|
|
185
|
-
# yarn
|
|
186
|
-
yarn add @poppinss/utils
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
and then use it as follows:
|
|
190
|
-
|
|
191
|
-
```ts
|
|
192
|
-
import { requireAll } from '@poppinss/utils'
|
|
193
|
-
requireAll(__dirname)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Exception
|
|
197
|
-
|
|
198
|
-
A custom exception class that extends the `Error` class to add support for defining `status` and `error codes`.
|
|
10
|
+
Many of my open source projects (including AdonisJS) use many single-purpose utility packages from npm. Over the years, I have faced the following challenges when using these packages.
|
|
199
11
|
|
|
200
|
-
|
|
201
|
-
|
|
12
|
+
- It takes a lot of time to find a perfect package for the use case. The package should be well maintained, have good test coverage, and not accumulate debt by supporting some old versions of Node.js.
|
|
13
|
+
- Some packages are great, but they end up pulling a lot of unnecessary dependencies like [(requiring TypeScript as a prod dependency)](https://github.com/blakeembrey/change-case/issues/281)
|
|
14
|
+
- Sometimes I end up using different packages for the same utility (because, I cannot remember what I used last time in that other package). So I want to spend time once choosing the one I need and then bundle it inside `@poppinss/utils`.
|
|
15
|
+
- Some authors introduce breaking changes too often (not a criticism). Therefore, I prefer wrapping their packages with my external API only to absorb breaking changes in one place.
|
|
16
|
+
- Rest are some handwritten utilities to fit my needs
|
|
202
17
|
|
|
203
|
-
|
|
204
|
-
throw new Exception('Route not found', 404, 'E_ROUTE_NOT_FOUND')
|
|
205
|
-
```
|
|
18
|
+
> **Note**: If you are creating an AdonisJS package, I highly recommend using this package since it is already part of the user's project dependencies.
|
|
206
19
|
|
|
207
|
-
|
|
20
|
+
> **Warning**: This package is not for general use (outside the AdonisJS ecosystem). I will not add new helpers or remove any to cater to a broader audience.
|
|
208
21
|
|
|
209
|
-
|
|
22
|
+
## Other packages to use
|
|
23
|
+
A note to self and others to consider the following packages.
|
|
210
24
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
**foo.js**
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
module.exports = {
|
|
219
|
-
greeting: 'Hello world',
|
|
220
|
-
}
|
|
221
|
-
```
|
|
25
|
+
| Package | Description |
|
|
26
|
+
|---------|-------------|
|
|
27
|
+
| [he](https://www.npmjs.com/package/he) | For escaping HTML entities and encoding unicode symbols. Has zero dependencies |
|
|
28
|
+
| [@sindresorhus/is](https://www.npmjs.com/package/@sindresorhus/is) | For advanced type checking. Has zero dependencies |
|
|
222
29
|
|
|
223
|
-
|
|
30
|
+
## Package size
|
|
31
|
+
Even though I do not care much about the package size (most of work is consumed on server side), I am mindful around the utilities and ensure not end up using really big packages for smaller use-cases.
|
|
224
32
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
```
|
|
33
|
+
| | |
|
|
34
|
+
|------------------|-----------------|
|
|
35
|
+
| Source code size | `272K` (approx) |
|
|
36
|
+
| Dependencies size | `432K` (approx) |
|
|
230
37
|
|
|
231
|
-
|
|
38
|
+
## Installation
|
|
232
39
|
|
|
233
|
-
|
|
234
|
-
export const greeting = {
|
|
235
|
-
greeting: 'hello world',
|
|
236
|
-
}
|
|
237
|
-
```
|
|
40
|
+
Install the package from the npm registry as follows:
|
|
238
41
|
|
|
239
|
-
```
|
|
240
|
-
|
|
42
|
+
```sh
|
|
43
|
+
npm i @poppinss/utils
|
|
241
44
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
esmRequire('./foo.esm.js') // { greeting: { greeting: 'hello world' } }
|
|
45
|
+
# Yarn lovers
|
|
46
|
+
yarn add @poppinss/utils
|
|
245
47
|
```
|
|
246
48
|
|
|
247
|
-
##
|
|
49
|
+
## Exported modules
|
|
248
50
|
|
|
249
|
-
|
|
51
|
+
Following are the exported modules. Only the generic helpers are shipped from the main path. The rest of the helpers are grouped inside sub-modules.
|
|
250
52
|
|
|
251
53
|
```ts
|
|
252
|
-
|
|
54
|
+
// string sub-module
|
|
55
|
+
import string from '@poppinss/utils/string'
|
|
253
56
|
|
|
254
|
-
|
|
57
|
+
// json sub-module
|
|
58
|
+
import json from '@poppinss/utils/json'
|
|
255
59
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
__esModule: true,
|
|
259
|
-
}) // { greeting: 'hello world' }
|
|
60
|
+
// lodash sub-module
|
|
61
|
+
import lodash from '@poppinss/utils/lodash'
|
|
260
62
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
__esModule: true,
|
|
264
|
-
}) // { greeting: { greeting: 'hello world' } }
|
|
63
|
+
// main module
|
|
64
|
+
import { base64, Exception, fsReadAll } from '@poppinss/utils'
|
|
265
65
|
```
|
|
266
66
|
|
|
267
|
-
|
|
67
|
+
### String helpers
|
|
268
68
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
Also, all of the lodash functions are published as individual modules on npm. However, most of those individual packages are outdated and using them is not an option.
|
|
272
|
-
|
|
273
|
-
Instead, we decided to pick some individual utilities that we need inside AdonisJS ecosystem and export them from the lodash package, as each function is exposed in its own separate file.
|
|
69
|
+
A collection of helpers to perform operations on/related to a string value.
|
|
274
70
|
|
|
275
71
|
```ts
|
|
276
|
-
import
|
|
277
|
-
lodash.get({ name: 'virk' }, 'name') // virk
|
|
72
|
+
import string from '@poppinss/utils/string'
|
|
278
73
|
```
|
|
279
74
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
Following is the list of exported helpers.
|
|
283
|
-
|
|
284
|
-
- [pick](https://lodash.com/docs/latest#pick)
|
|
285
|
-
- [omit](https://lodash.com/docs/latest#omit)
|
|
286
|
-
- [has](https://lodash.com/docs/latest#has)
|
|
287
|
-
- [get](https://lodash.com/docs/latest#get)
|
|
288
|
-
- [set](https://lodash.com/docs/latest#set)
|
|
289
|
-
- [unset](https://lodash.com/docs/latest#unset)
|
|
290
|
-
- [mergeWith](https://lodash.com/docs/latest#mergeWith)
|
|
291
|
-
- [merge](https://lodash.com/docs/latest#merge)
|
|
292
|
-
- [size](https://lodash.com/docs/latest#size)
|
|
293
|
-
- [clone](https://lodash.com/docs/latest#clone)
|
|
294
|
-
- [cloneDeep](https://lodash.com/docs/latest#cloneDeep)
|
|
295
|
-
|
|
296
|
-
## Safe stringify
|
|
75
|
+
#### excerpt
|
|
297
76
|
|
|
298
|
-
|
|
77
|
+
Generate an excerpt from a string value. If the input value contains HTML tags, we will remove them from the excerpt.
|
|
299
78
|
|
|
300
79
|
```ts
|
|
301
|
-
|
|
80
|
+
const html = `<p>AdonisJS is a Node.js framework, and hence it requires Node.js to be installed on your computer. To be precise, we need at least the latest release of <code>Node.js v14</code>.</p>`
|
|
302
81
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
console.log(safeStringify(o))
|
|
307
|
-
// { "b":1,"a":0 }
|
|
308
|
-
|
|
309
|
-
console.log(JSON.stringify(o))
|
|
310
|
-
// TypeError: Converting circular structure to JSON
|
|
82
|
+
console.log(string.excerpt(html, 70))
|
|
83
|
+
// AdonisJS is a Node.js framework, and hence it requires Node.js to be i...
|
|
311
84
|
```
|
|
312
85
|
|
|
313
|
-
|
|
86
|
+
| Argument | Type | Description |
|
|
87
|
+
|---------|------|---------------|
|
|
88
|
+
| `sentence` | string | The value for which to generate excerpt |
|
|
89
|
+
| `charactersLimit` | string | The number of characters to keep |
|
|
90
|
+
| `options.completeWords` | boolean | When set to `true`, the truncation will happen only after complete words. This option might go over the defined characters limit
|
|
91
|
+
| `options.suffix` | string | The value to append after the truncated string. Defaults to three dots `...` |
|
|
314
92
|
|
|
315
|
-
|
|
93
|
+
#### truncate
|
|
94
|
+
Truncate a string value to a certain length. The method is the same as the `excerpt` method but does not remove any HTML tags. It is a great fit when you are truncating a non-HTML string.
|
|
316
95
|
|
|
317
96
|
```ts
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const input = '{ "user": { "__proto__": { "isAdmin": true } } }'
|
|
321
|
-
|
|
322
|
-
JSON.parse(input)
|
|
323
|
-
// { user: { __proto__: { isAdmin: true } } }
|
|
97
|
+
const text = `AdonisJS is a Node.js framework, and hence it requires Node.js to be installed on your computer. To be precise, we need at least the latest release of Node.js 14.`
|
|
324
98
|
|
|
325
|
-
|
|
326
|
-
//
|
|
99
|
+
console.log(string.truncate(text, 70))
|
|
100
|
+
// AdonisJS is a Node.js framework, and hence it requires Node.js to be i...
|
|
327
101
|
```
|
|
328
102
|
|
|
329
|
-
|
|
103
|
+
| Argument | Type | Description |
|
|
104
|
+
|---------|------|---------------|
|
|
105
|
+
| `sentence` | string | The value to truncate |
|
|
106
|
+
| `charactersLimit` | string | The number of characters to keep |
|
|
107
|
+
| `options.completeWords` | boolean | When set to `true`, the truncation will happen only after complete words. This option might go over the defined characters limit
|
|
108
|
+
| `options.suffix` | string | The value to append after the truncated string. Defaults to three dots `...` |
|
|
330
109
|
|
|
331
|
-
|
|
110
|
+
#### slug
|
|
111
|
+
Generate slug for a string value. The method is exported directly from the [slugify](https://www.npmjs.com/package/slugify) package.
|
|
332
112
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
You create an application wide base model
|
|
113
|
+
Please check the package documentation for [available options](https://www.npmjs.com/package/slugify#options).
|
|
336
114
|
|
|
337
115
|
```ts
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
public createdAt: DateTime
|
|
341
|
-
}
|
|
116
|
+
console.log(string.slug('hello ♥ world'))
|
|
117
|
+
// hello-love-world
|
|
342
118
|
```
|
|
343
119
|
|
|
344
|
-
|
|
120
|
+
You can add custom replacements for Unicode values as follows.
|
|
345
121
|
|
|
346
122
|
```ts
|
|
347
|
-
|
|
123
|
+
string.slug.extend({ '☢': 'radioactive' })
|
|
124
|
+
|
|
125
|
+
console.log(string.slug('unicode ♥ is ☢'))
|
|
126
|
+
// unicode-love-is-radioactive
|
|
348
127
|
```
|
|
349
128
|
|
|
350
|
-
|
|
129
|
+
#### interpolate
|
|
130
|
+
Interpolate variables inside a string. The variables must be inside double curly braces.
|
|
351
131
|
|
|
352
132
|
```ts
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
```
|
|
133
|
+
string.interpolate(
|
|
134
|
+
'hello {{ user.username }}',
|
|
135
|
+
{ user: { username: 'virk' } }
|
|
136
|
+
)
|
|
358
137
|
|
|
359
|
-
|
|
138
|
+
// hello virk
|
|
139
|
+
```
|
|
360
140
|
|
|
361
|
-
|
|
141
|
+
You can also replace array values by mentioning the array index.
|
|
362
142
|
|
|
363
143
|
```ts
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
defaultValue: {},
|
|
369
|
-
strategy: 'inherit',
|
|
370
|
-
})
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
```
|
|
144
|
+
string.interpolate(
|
|
145
|
+
'hello {{ users.0 }}',
|
|
146
|
+
{ users: ['virk'] }
|
|
147
|
+
)
|
|
374
148
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
- The first argument is always `this`.
|
|
378
|
-
- The second argument is the root level base class. This will usually be the class exported by your package or module.
|
|
379
|
-
- The third argument takes the `propertyName`, `defaultValue (in case, there is nothing to copy)`, and the `strategy`.
|
|
380
|
-
- The `inherit` strategy will copy the properties from the base class.
|
|
381
|
-
- The `define` strategy will always use the `defaultValue` to define the property on the class. In other words, there is no copy behavior, but prototypal inheritance chain is also breaked by explicitly re-defining the property.
|
|
149
|
+
// hello virk
|
|
150
|
+
```
|
|
382
151
|
|
|
383
|
-
|
|
384
|
-
Flatten an object/array. The method wraps the [flattie](https://github.com/lukeed/flattie) package.
|
|
152
|
+
You can escape the curly braces by prefixing them with `\\`.
|
|
385
153
|
|
|
386
154
|
```ts
|
|
387
|
-
|
|
155
|
+
string.interpolate('hello \\{{ users.0 }}', {})
|
|
388
156
|
|
|
389
|
-
|
|
390
|
-
a: 'hi',
|
|
391
|
-
b: {
|
|
392
|
-
a: null,
|
|
393
|
-
b: ['foo', '', null, 'bar'],
|
|
394
|
-
d: 'hello',
|
|
395
|
-
e: {
|
|
396
|
-
a: 'yo',
|
|
397
|
-
b: undefined,
|
|
398
|
-
c: 'sup',
|
|
399
|
-
d: 0,
|
|
400
|
-
f: [
|
|
401
|
-
{ foo: 123, bar: 123 },
|
|
402
|
-
{ foo: 465, bar: 456 },
|
|
403
|
-
]
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
c: 'world'
|
|
407
|
-
});
|
|
408
|
-
// {
|
|
409
|
-
// 'a': 'hi',
|
|
410
|
-
// 'b.b.0': 'foo',
|
|
411
|
-
// 'b.b.1': '',
|
|
412
|
-
// 'b.b.3': 'bar',
|
|
413
|
-
// 'b.d': 'hello',
|
|
414
|
-
// 'b.e.a': 'yo',
|
|
415
|
-
// 'b.e.c': 'sup',
|
|
416
|
-
// 'b.e.d': 0,
|
|
417
|
-
// 'b.e.f.0.foo': 123,
|
|
418
|
-
// 'b.e.f.0.bar': 123,
|
|
419
|
-
// 'b.e.f.1.foo': 465,
|
|
420
|
-
// 'b.e.f.1.bar': 456,
|
|
421
|
-
// 'c': 'world'
|
|
422
|
-
// }
|
|
157
|
+
// hello {{ users.0 }}
|
|
423
158
|
```
|
|
424
159
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
The helpers module is also available in AdonisJS applications as follows:
|
|
160
|
+
#### plural
|
|
161
|
+
Convert a word to its plural form. The method is exported directly from the [pluralize](https://www.npmjs.com/package/pluralize) package.
|
|
428
162
|
|
|
429
163
|
```ts
|
|
430
|
-
|
|
164
|
+
string.plural('test')
|
|
165
|
+
// tests
|
|
431
166
|
```
|
|
432
167
|
|
|
433
|
-
|
|
168
|
+
#### singular
|
|
169
|
+
Convert a word to its singular form. The method is exported directly from the [pluralize](https://www.npmjs.com/package/pluralize) package.
|
|
434
170
|
|
|
435
171
|
```ts
|
|
436
|
-
|
|
172
|
+
string.singular('tests')
|
|
173
|
+
// test
|
|
437
174
|
```
|
|
438
175
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
A utility to recursively read all script files for a given directory. This method is equivalent to
|
|
442
|
-
`readdir + recursive + filter (.js, .json, .ts)`.
|
|
176
|
+
#### pluralize
|
|
177
|
+
This method combines the `singular` and `plural` methods and uses one or the other based on the count. For example:
|
|
443
178
|
|
|
444
179
|
```ts
|
|
445
|
-
|
|
180
|
+
string.pluralize('box', 1) // box
|
|
181
|
+
string.pluralize('box', 2) // boxes
|
|
182
|
+
string.pluralize('box', 0) // boxes
|
|
446
183
|
|
|
447
|
-
|
|
184
|
+
string.pluralize('boxes', 1) // box
|
|
185
|
+
string.pluralize('boxes', 2) // boxes
|
|
186
|
+
string.pluralize('boxes', 0) // boxes
|
|
448
187
|
```
|
|
449
188
|
|
|
450
|
-
|
|
189
|
+
The `addPluralRule`, `addSingularRule`, `addIrregularRule`, and `addUncountableRule` methods exposed by the pluralize package can be called as follows.
|
|
451
190
|
|
|
452
191
|
```ts
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
})
|
|
192
|
+
string.pluralize.addUncountableRule('paper')
|
|
193
|
+
string.pluralize.addSingularRule(/singles$/i, 'singular')
|
|
456
194
|
```
|
|
457
195
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
Same as `fsReadAll`, but instead require the files. Helpful when you want to load all the config files inside a directory on app boot.
|
|
196
|
+
#### isPlural
|
|
197
|
+
Find if a word is already in plural form. The method is exported directly from the [pluralize](https://www.npmjs.com/package/pluralize) package.
|
|
461
198
|
|
|
462
199
|
```ts
|
|
463
|
-
|
|
464
|
-
const config = requireAll(join(__dirname, 'config'))
|
|
465
|
-
|
|
466
|
-
{
|
|
467
|
-
file1: {}, // exported object
|
|
468
|
-
file2: {} // exported object
|
|
469
|
-
}
|
|
200
|
+
string.isPlural('tests') // true
|
|
470
201
|
```
|
|
471
202
|
|
|
472
|
-
|
|
203
|
+
#### isSingular
|
|
204
|
+
Find if a word is already in a singular form. The method is exported directly from the [pluralize](https://www.npmjs.com/package/pluralize) package.
|
|
473
205
|
|
|
474
206
|
```ts
|
|
475
|
-
|
|
207
|
+
string.isSingular('test') // true
|
|
476
208
|
```
|
|
477
209
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
- `filter` Cherry pick files to require. By default, all JavaScript, TypeScript and JSON files are required.
|
|
481
|
-
|
|
482
|
-
### resolveFrom
|
|
483
|
-
|
|
484
|
-
Works similar to `require.resolve`, however it handles the absolute paths properly.
|
|
210
|
+
#### camelCase
|
|
211
|
+
Convert a string value to camelcase.
|
|
485
212
|
|
|
486
213
|
```ts
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
resolveFrom(__dirname, 'npm-package') // returns path to package "main" file
|
|
490
|
-
resolveFrom(__dirname, './foo.js') // returns path to `foo.js` (if exists)
|
|
491
|
-
resolveFrom(__dirname, join(__dirname, './foo.js')) // returns path to `foo.js` (if exists)
|
|
214
|
+
string.camelCase('user_name') // userName
|
|
492
215
|
```
|
|
493
216
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
The `require.resolve` or `resolveFrom` method can only resolve paths to a given file and not the directory. For example: If you pass path to a directory, then it will search for `index.js` inside it and in case of a package, it will be search for `main` entry point.
|
|
217
|
+
Following are some of the conversion examples.
|
|
497
218
|
|
|
498
|
-
|
|
219
|
+
| Input | Output |
|
|
220
|
+
|------|---------|
|
|
221
|
+
| 'test' | 'test' |
|
|
222
|
+
| 'test string' | 'testString' |
|
|
223
|
+
| 'Test String' | 'testString' |
|
|
224
|
+
| 'TestV2' | 'testV2' |
|
|
225
|
+
| '_foo_bar_' | 'fooBar' |
|
|
226
|
+
| 'version 1.2.10' | 'version1210' |
|
|
227
|
+
| 'version 1.21.0' | 'version1210' |
|
|
499
228
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
- Path to packages inside `node_modules` are resolved as follows: - Uses `require.resolve` to resolve the `package.json` file. - Then replace the `package-name` with the absolute resolved package path.
|
|
229
|
+
#### capitalCase
|
|
230
|
+
Convert a string value to a capital case.
|
|
503
231
|
|
|
504
232
|
```ts
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
resolveDir(__dirname, './database/migrations')
|
|
508
|
-
// __dirname + /database/migrations
|
|
509
|
-
|
|
510
|
-
resolveDir(__dirname, 'some-package/database/migrations')
|
|
511
|
-
// {path-to-package}/database/migrations
|
|
512
|
-
|
|
513
|
-
resolveDir(__dirname, '@some/package/database/migrations')
|
|
514
|
-
// {path-to-package}/database/migrations
|
|
233
|
+
string.capitalCase('helloWorld') // Hello World
|
|
515
234
|
```
|
|
516
235
|
|
|
517
|
-
|
|
236
|
+
Following are some of the conversion examples.
|
|
518
237
|
|
|
519
|
-
|
|
238
|
+
| Input | Output |
|
|
239
|
+
|------|---------|
|
|
240
|
+
| 'test' | 'Test' |
|
|
241
|
+
| 'test string' | 'Test String' |
|
|
242
|
+
| 'Test String' | 'Test String' |
|
|
243
|
+
| 'TestV2' | 'Test V 2' |
|
|
244
|
+
| 'version 1.2.10' | 'Version 1.2.10' |
|
|
245
|
+
| 'version 1.21.0' | 'Version 1.21.0' |
|
|
520
246
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
interpolate('hello {{ username }}', {
|
|
525
|
-
username: 'virk',
|
|
526
|
-
})
|
|
247
|
+
#### dashCase
|
|
248
|
+
Convert a string value to a dash case.
|
|
527
249
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
})
|
|
250
|
+
```ts
|
|
251
|
+
string.dashCase('helloWorld') // hello-world
|
|
531
252
|
```
|
|
532
253
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
Use the `\` to escape a mustache block from getting evaluated.
|
|
254
|
+
Optionally, you can capitalize the first letter of each word.
|
|
536
255
|
|
|
537
256
|
```ts
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
interpolate('\\{{ username }} expression evaluates to {{ username }}', {
|
|
541
|
-
username: 'virk',
|
|
542
|
-
})
|
|
543
|
-
// Output: {{ username }} expression evaluates to virk
|
|
257
|
+
string.dashCase('helloWorld', { capitalize: true }) // Hello-World
|
|
544
258
|
```
|
|
545
259
|
|
|
546
|
-
|
|
260
|
+
Following are some of the conversion examples.
|
|
547
261
|
|
|
548
|
-
|
|
262
|
+
| Input | Output |
|
|
263
|
+
|--------|--------|
|
|
264
|
+
| 'test' | 'test' |
|
|
265
|
+
| 'test string' | 'test-string' |
|
|
266
|
+
| 'Test String' | 'test-string' |
|
|
267
|
+
| 'Test V2' | 'test-v2' |
|
|
268
|
+
| 'TestV2' | 'test-v-2' |
|
|
269
|
+
| 'version 1.2.10' | 'version-1210' |
|
|
270
|
+
| 'version 1.21.0' | 'version-1210' |
|
|
549
271
|
|
|
550
|
-
####
|
|
272
|
+
#### dotCase
|
|
273
|
+
Convert a string value to a dot case.
|
|
551
274
|
|
|
552
275
|
```ts
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
base64.encode('hello world')
|
|
556
|
-
base64.encode(Buffer.from('hello world', 'binary'))
|
|
276
|
+
string.dotCase('helloWorld') // hello.World
|
|
557
277
|
```
|
|
558
278
|
|
|
559
|
-
|
|
279
|
+
Optionally, you can also convert the first letter of all the words to lowercase.
|
|
560
280
|
|
|
561
281
|
```ts
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
base64.decode(base64.encode('hello world'))
|
|
565
|
-
base64.decode(base64.encode(Buffer.from('hello world', 'binary')), 'binary')
|
|
282
|
+
string.dotCase('helloWorld', { lowerCase: true }) // hello.world
|
|
566
283
|
```
|
|
567
284
|
|
|
568
|
-
|
|
285
|
+
Following are some of the conversion examples.
|
|
569
286
|
|
|
570
|
-
|
|
287
|
+
| Input | Output |
|
|
288
|
+
|--------|--------|
|
|
289
|
+
| 'test' | 'test' |
|
|
290
|
+
| 'test string' | 'test.string' |
|
|
291
|
+
| 'Test String' | 'Test.String' |
|
|
292
|
+
| 'dot.case' | 'dot.case' |
|
|
293
|
+
| 'path/case' | 'path.case' |
|
|
294
|
+
| 'TestV2' | 'Test.V.2' |
|
|
295
|
+
| 'version 1.2.10' | 'version.1210' |
|
|
296
|
+
| 'version 1.21.0' | 'version.1210' |
|
|
571
297
|
|
|
572
|
-
####
|
|
298
|
+
#### noCase
|
|
299
|
+
Remove all sorts of casing from a string value.
|
|
573
300
|
|
|
574
|
-
|
|
301
|
+
```ts
|
|
302
|
+
string.noCase('helloWorld') // hello world
|
|
303
|
+
```
|
|
575
304
|
|
|
576
|
-
|
|
305
|
+
Following are some of the conversion examples.
|
|
306
|
+
|
|
307
|
+
| Input | Output |
|
|
308
|
+
|--------|--------|
|
|
309
|
+
| 'test' | 'test' |
|
|
310
|
+
| 'TEST' | 'test' |
|
|
311
|
+
| 'testString' | 'test string' |
|
|
312
|
+
| 'testString123' | 'test string123' |
|
|
313
|
+
| 'testString_1_2_3' | 'test string 1 2 3' |
|
|
314
|
+
| 'ID123String' | 'id123 string' |
|
|
315
|
+
| 'foo bar123' | 'foo bar123' |
|
|
316
|
+
| 'a1bStar' | 'a1b star' |
|
|
317
|
+
| 'CONSTANT_CASE ' | 'constant case' |
|
|
318
|
+
| 'CONST123_FOO' | 'const123 foo' |
|
|
319
|
+
| 'FOO_bar' | 'foo bar' |
|
|
320
|
+
| 'XMLHttpRequest' | 'xml http request' |
|
|
321
|
+
| 'IQueryAArgs' | 'i query a args' |
|
|
322
|
+
| 'dot.case' | 'dot case' |
|
|
323
|
+
| 'path/case' | 'path case' |
|
|
324
|
+
| 'snake_case' | 'snake case' |
|
|
325
|
+
| 'snake_case123' | 'snake case123' |
|
|
326
|
+
| 'snake_case_123' | 'snake case 123' |
|
|
327
|
+
| '"quotes"' | 'quotes' |
|
|
328
|
+
| 'version 0.45.0' | 'version 0 45 0' |
|
|
329
|
+
| 'version 0..78..9' | 'version 0 78 9' |
|
|
330
|
+
| 'version 4_99/4' | 'version 4 99 4' |
|
|
331
|
+
| ' test ' | 'test' |
|
|
332
|
+
| 'something_2014_other' | 'something 2014 other' |
|
|
333
|
+
| 'amazon s3 data' | 'amazon s3 data' |
|
|
334
|
+
| 'foo_13_bar' | 'foo 13 bar' |
|
|
577
335
|
|
|
578
|
-
|
|
336
|
+
#### pascalCase
|
|
337
|
+
Convert a string value to pascal case. Great for generating JavaScript class names.
|
|
579
338
|
|
|
580
339
|
```ts
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (safeValue('foo', 'foo')) {
|
|
584
|
-
}
|
|
340
|
+
string.pascalCase('user team') // UserTeam
|
|
585
341
|
```
|
|
586
342
|
|
|
587
|
-
|
|
343
|
+
Following are some of the conversion examples.
|
|
588
344
|
|
|
589
|
-
|
|
345
|
+
| Input | Output |
|
|
346
|
+
|--------|--------|
|
|
347
|
+
| 'test' | 'Test' |
|
|
348
|
+
| 'test string' | 'TestString' |
|
|
349
|
+
| 'Test String' | 'TestString' |
|
|
350
|
+
| 'TestV2' | 'TestV2' |
|
|
351
|
+
| 'version 1.2.10' | 'Version1210' |
|
|
352
|
+
| 'version 1.21.0' | 'Version1210' |
|
|
590
353
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
The message builder alone may seem useless, since anyone can decode the object and change its expiry or purpose. However, you can generate an hash of the stringified object and verify the tampering by validating the hash. This is what AdonisJS does for cookies.
|
|
354
|
+
#### sentenceCase
|
|
355
|
+
Convert a value to a sentence.
|
|
595
356
|
|
|
596
357
|
```ts
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const builder = new MessageBuilder()
|
|
600
|
-
const encoded = builder.build({ username: 'virk' }, '1 hour', 'login')
|
|
358
|
+
string.sentenceCase('getting-started-with-adonisjs')
|
|
359
|
+
// Getting started with adonisjs
|
|
601
360
|
```
|
|
602
361
|
|
|
603
|
-
|
|
362
|
+
Following are some of the conversion examples.
|
|
363
|
+
|
|
364
|
+
| Input | Output |
|
|
365
|
+
|--------|--------|
|
|
366
|
+
| 'test' | 'Test' |
|
|
367
|
+
| 'test string' | 'Test string' |
|
|
368
|
+
| 'Test String' | 'Test string' |
|
|
369
|
+
| 'TestV2' | 'Test v2' |
|
|
370
|
+
| 'version 1.2.10' | 'Version 1 2 10' |
|
|
371
|
+
| 'version 1.21.0' | 'Version 1 21 0' |
|
|
372
|
+
|
|
373
|
+
#### snakeCase
|
|
374
|
+
Convert value to snake case.
|
|
604
375
|
|
|
605
376
|
```ts
|
|
606
|
-
|
|
607
|
-
builder.verify(encoded, 'register') // returns null, purpose mismatch.
|
|
608
|
-
builder.verify(encoded, 'login') // return { username: 'virk' }
|
|
377
|
+
string.snakeCase('user team') // user_team
|
|
609
378
|
```
|
|
610
379
|
|
|
611
|
-
|
|
380
|
+
Following are some of the conversion examples.
|
|
612
381
|
|
|
613
|
-
|
|
382
|
+
| Input | Output |
|
|
383
|
+
|--------|--------|
|
|
384
|
+
| '_id' | 'id' |
|
|
385
|
+
| 'test' | 'test' |
|
|
386
|
+
| 'test string' | 'test_string' |
|
|
387
|
+
| 'Test String' | 'test_string' |
|
|
388
|
+
| 'Test V2' | 'test_v2' |
|
|
389
|
+
| 'TestV2' | 'test_v_2' |
|
|
390
|
+
| 'version 1.2.10' | 'version_1210' |
|
|
391
|
+
| 'version 1.21.0' | 'version_1210' |
|
|
614
392
|
|
|
615
|
-
|
|
393
|
+
#### titleCase
|
|
394
|
+
Convert a string value to title case.
|
|
616
395
|
|
|
617
396
|
```ts
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
const UserWithEmail = <T extends Constructor>(superclass: T) => {
|
|
621
|
-
return class extends superclass {
|
|
622
|
-
public email: string
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
const UserWithPassword = <T extends Constructor>(superclass: T) => {
|
|
627
|
-
return class extends superclass {
|
|
628
|
-
public password: string
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
class BaseModel {}
|
|
633
|
-
class User extends UserWithPassword(UserWithEmail(BaseModel)) {}
|
|
397
|
+
string.titleCase('small word ends on')
|
|
398
|
+
// Small Word Ends On
|
|
634
399
|
```
|
|
635
400
|
|
|
636
|
-
|
|
401
|
+
Following are some of the conversion examples.
|
|
402
|
+
|
|
403
|
+
| Input | Output |
|
|
404
|
+
|--------|--------|
|
|
405
|
+
| 'one. two.' | 'One. Two.' |
|
|
406
|
+
| 'a small word starts' | 'A Small Word Starts' |
|
|
407
|
+
| 'small word ends on' | 'Small Word Ends On' |
|
|
408
|
+
| 'we keep NASA capitalized' | 'We Keep NASA Capitalized' |
|
|
409
|
+
| 'pass camelCase through' | 'Pass camelCase Through' |
|
|
410
|
+
| 'follow step-by-step instructions' | 'Follow Step-by-Step Instructions' |
|
|
411
|
+
| 'this vs. that' | 'This vs. That' |
|
|
412
|
+
| 'this vs that' | 'This vs That' |
|
|
413
|
+
| 'newcastle upon tyne' | 'Newcastle upon Tyne' |
|
|
414
|
+
| 'newcastle \*upon\* tyne' | 'Newcastle \*upon\* Tyne' |
|
|
637
415
|
|
|
638
|
-
|
|
416
|
+
#### random
|
|
417
|
+
Generate a cryptographically secure random string of a given length. The output value is URL safe base64 encoded string.
|
|
639
418
|
|
|
640
419
|
```ts
|
|
641
|
-
|
|
420
|
+
string.random(32)
|
|
421
|
+
// 8mejfWWbXbry8Rh7u8MW3o-6dxd80Thk
|
|
642
422
|
```
|
|
643
423
|
|
|
644
|
-
|
|
424
|
+
#### toSentence
|
|
425
|
+
Convert an array of words to a comma-separated sentence.
|
|
645
426
|
|
|
646
427
|
```ts
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
class User extends compose(
|
|
650
|
-
BaseModel,
|
|
651
|
-
UserWithPassword,
|
|
652
|
-
UserWithEmail,
|
|
653
|
-
UserWithAge,
|
|
654
|
-
UserWithAttributes
|
|
655
|
-
) {}
|
|
428
|
+
string.toSentence(['routes', 'controllers', 'middleware'])
|
|
429
|
+
// routes, controllers, and middleware
|
|
656
430
|
```
|
|
657
431
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
Typescript has an [open issue](https://github.com/microsoft/TypeScript/issues/37142) related to the constructor arguments of the mixin class or the base class.
|
|
661
|
-
|
|
662
|
-
Typescript expects all classes used in the mixin chain to have a constructor with only one argument of `...args: any[]`. For example: The following code will work fine at runtime, but the **typescript compiler complains about it**.
|
|
432
|
+
You can replace the `and` with an `or` by specifying the `options.lastSeparator` property.
|
|
663
433
|
|
|
664
434
|
```ts
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const UserWithEmail = <T extends typeof BaseModel>(superclass: T) => {
|
|
670
|
-
return class extends superclass {
|
|
671
|
-
// ERROR: A mixin class must have a constructor with a single rest parameter of type 'any[]'.ts(2545)
|
|
672
|
-
public email: string
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
class User extends compose(BaseModel, UserWithEmail) {}
|
|
435
|
+
string.toSentence(['routes', 'controllers', 'middleware'], {
|
|
436
|
+
lastSeparator: ', or '
|
|
437
|
+
})
|
|
677
438
|
```
|
|
678
439
|
|
|
679
|
-
|
|
440
|
+
In the following example, the two words are combined using the `and` separator, not the comma (usually advocated in English). However, you can use a custom separator for a pair of words.
|
|
680
441
|
|
|
681
442
|
```ts
|
|
682
|
-
|
|
443
|
+
string.toSentence(['routes', 'controllers'])
|
|
444
|
+
// routes and controllers
|
|
683
445
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
446
|
+
string.toSentence(['routes', 'controllers'], {
|
|
447
|
+
pairSeparator: ', and '
|
|
448
|
+
})
|
|
449
|
+
// routes, and controllers
|
|
689
450
|
```
|
|
690
451
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
The `string` module includes a bunch of helper methods to work with strings.
|
|
694
|
-
|
|
695
|
-
#### camelCase
|
|
696
|
-
|
|
697
|
-
Convert a string to its `camelCase` version.
|
|
452
|
+
#### condenseWhitespace
|
|
453
|
+
Remove multiple whitespaces from a string to a single whitespace.
|
|
698
454
|
|
|
699
455
|
```ts
|
|
700
|
-
|
|
456
|
+
string.condenseWhitespace('hello world')
|
|
457
|
+
// hello world
|
|
701
458
|
|
|
702
|
-
string.
|
|
459
|
+
string.condenseWhitespace(' hello world ')
|
|
460
|
+
// hello world
|
|
703
461
|
```
|
|
704
462
|
|
|
705
|
-
####
|
|
706
|
-
|
|
707
|
-
Convert a string to its `snake_case` version.
|
|
463
|
+
#### ordinal
|
|
464
|
+
Get the ordinal letter for a given number.
|
|
708
465
|
|
|
709
466
|
```ts
|
|
710
|
-
|
|
467
|
+
string.ordinal(1) // 1st
|
|
468
|
+
string.ordinal(2) // '2nd'
|
|
469
|
+
string.ordinal(3) // '3rd'
|
|
470
|
+
string.ordinal(4) // '4th'
|
|
711
471
|
|
|
712
|
-
string.
|
|
472
|
+
string.ordinal(23) // '23rd'
|
|
473
|
+
string.ordinal(24) // '24th'
|
|
713
474
|
```
|
|
714
475
|
|
|
715
|
-
####
|
|
716
|
-
|
|
717
|
-
Convert a string to its `dash-case` version. Optionally, you can also capitalize the first letter of each segment.
|
|
476
|
+
#### seconds.(parse/format)
|
|
477
|
+
Parse a string-based time expression to seconds.
|
|
718
478
|
|
|
719
479
|
```ts
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
string.dashCase('helloWorld') // hello-world
|
|
723
|
-
string.dashCase('helloWorld', { capitalize: true }) // Hello-World
|
|
480
|
+
string.seconds.parse('10h') // 36000
|
|
481
|
+
string.seconds.parse('1 day') // 86400
|
|
724
482
|
```
|
|
725
483
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
Convert a string to its `PascalCase` version.
|
|
484
|
+
Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in seconds.
|
|
729
485
|
|
|
730
486
|
```ts
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
string.pascalCase('helloWorld') // HelloWorld
|
|
487
|
+
string.seconds.parse(180) // 180
|
|
734
488
|
```
|
|
735
489
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
Capitalize a string
|
|
490
|
+
You can format seconds to a pretty string using the `format` method.
|
|
739
491
|
|
|
740
492
|
```ts
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
string.capitalCase('helloWorld') // Hello World
|
|
493
|
+
string.seconds.format(36000) // 10h
|
|
494
|
+
string.seconds.format(36000, true) // 10 hours
|
|
744
495
|
```
|
|
745
496
|
|
|
746
|
-
####
|
|
747
|
-
|
|
748
|
-
Convert string to a sentence
|
|
497
|
+
#### milliseconds.(parse/format)
|
|
498
|
+
Parse a string-based time expression to milliseconds.
|
|
749
499
|
|
|
750
500
|
```ts
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
string.sentenceCase('hello-world') // Hello world
|
|
501
|
+
string.milliseconds.parse('1 h') // 3.6e6
|
|
502
|
+
string.milliseconds.parse('1 day') // 8.64e7
|
|
754
503
|
```
|
|
755
504
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
Convert string to its `dot.case` version.
|
|
505
|
+
Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in milliseconds.
|
|
759
506
|
|
|
760
507
|
```ts
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
string.dotCase('hello-world') // hello.world
|
|
508
|
+
string.milliseconds.parse(180) // 180
|
|
764
509
|
```
|
|
765
510
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
Remove all sorts of casing
|
|
511
|
+
Using the `format` method, you can format milliseconds to a pretty string.
|
|
769
512
|
|
|
770
513
|
```ts
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
string.noCase('hello-world') // hello world
|
|
774
|
-
string.noCase('hello_world') // hello world
|
|
775
|
-
string.noCase('helloWorld') // hello world
|
|
514
|
+
string.seconds.format(3.6e6) // 1h
|
|
515
|
+
string.seconds.format(3.6e6, true) // 1 hour
|
|
776
516
|
```
|
|
777
517
|
|
|
778
|
-
####
|
|
779
|
-
|
|
780
|
-
Convert a sentence to title case
|
|
518
|
+
#### bytes.(parse/format)
|
|
519
|
+
Parse a string-based unit expression to bytes.
|
|
781
520
|
|
|
782
521
|
```ts
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
string.titleCase('Here is a fox') // Here Is a fox
|
|
522
|
+
string.bytes.parse('1KB') // 1024
|
|
523
|
+
string.bytes.parse('1MB') // 1048576
|
|
786
524
|
```
|
|
787
525
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
Pluralize a word.
|
|
526
|
+
Passing a numeric value to the `parse` method is returned as it is, assuming the value is already in bytes.
|
|
791
527
|
|
|
792
528
|
```ts
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
string.pluralize('box') // boxes
|
|
796
|
-
string.pluralize('i') // we
|
|
529
|
+
string.bytes.parse(1024) // 1024
|
|
797
530
|
```
|
|
798
531
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
- The first argument is the singular variation
|
|
802
|
-
- The second argument is the plural variation
|
|
532
|
+
Using the `format` method, you can format bytes to a pretty string. The method is exported directly from the [bytes](https://www.npmjs.com/package/bytes) package. Please reference the package README for available options.
|
|
803
533
|
|
|
804
534
|
```ts
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
string.
|
|
808
|
-
string.plural('auth') // auth
|
|
535
|
+
string.bytes.format(1048576) // 1MB
|
|
536
|
+
string.bytes.format(1024 * 1024 * 1000) // 1000MB
|
|
537
|
+
string.bytes.format(1024 * 1024 * 1000, { thousandsSeparator: ',' }) // 1,000MB
|
|
809
538
|
```
|
|
810
539
|
|
|
811
|
-
|
|
540
|
+
### JSON helpers
|
|
541
|
+
Following are the helpers we use to `stringify` and `parse` JSON.
|
|
812
542
|
|
|
813
|
-
|
|
814
|
-
|
|
543
|
+
#### safeParse
|
|
544
|
+
The native implementation of `JSON.parse` opens up the possibility for [prototype poisoning](https://medium.com/intrinsic-blog/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96). The `safeParse` method removes the `__proto__` and the `constructor.prototype` properties from the JSON string at the time of parsing it.
|
|
815
545
|
|
|
816
|
-
|
|
817
|
-
string.plural('login') // home
|
|
818
|
-
```
|
|
546
|
+
The method is a wrapper over [secure-json-parse](https://github.com/fastify/secure-json-parse) package.
|
|
819
547
|
|
|
820
|
-
####
|
|
548
|
+
#### safeStringify
|
|
549
|
+
The native implementation of `JSON.stringify` cannot handle circular references or language-specific data types like `BigInt`.
|
|
821
550
|
|
|
822
|
-
|
|
551
|
+
Therefore, we use the [safe-stable-stringify](https://github.com/BridgeAR/safe-stable-stringify) package under the hood to overcome the limitations of native implementation.
|
|
823
552
|
|
|
824
553
|
```ts
|
|
825
|
-
import {
|
|
554
|
+
import { safeStringify } from '@poppinss/utils/json'
|
|
826
555
|
|
|
827
|
-
|
|
828
|
-
|
|
556
|
+
const value = {
|
|
557
|
+
b: 2,
|
|
558
|
+
c: BigInt(10),
|
|
559
|
+
}
|
|
829
560
|
|
|
830
|
-
|
|
561
|
+
// Circular reference
|
|
562
|
+
value.a = value
|
|
831
563
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
completeWords: true,
|
|
835
|
-
}) // This is a very...
|
|
564
|
+
safeStringify(value)
|
|
565
|
+
// '{"b":2,"c":10}'
|
|
836
566
|
```
|
|
837
567
|
|
|
838
|
-
|
|
568
|
+
- The circular references are removed from the final JSON string.
|
|
569
|
+
- The BigInt values are converted to a string.
|
|
839
570
|
|
|
840
|
-
|
|
841
|
-
string.truncate('This is a very long, maybe not that long title', 12, {
|
|
842
|
-
completeWords: true,
|
|
843
|
-
suffix: ' <a href="/1"> Read more </a>',
|
|
844
|
-
}) // This is a very <a href="/1"> Read more </a>
|
|
845
|
-
```
|
|
571
|
+
The `safeStringify` API is the same as the `JSON.stringify` method.
|
|
846
572
|
|
|
847
|
-
|
|
573
|
+
- You can pass a replacer function as the second parameter.
|
|
574
|
+
- And number of spaces as the third parameter.
|
|
848
575
|
|
|
849
|
-
|
|
576
|
+
### Lodash helpers
|
|
577
|
+
Lodash is quite a big library, and we do not use all its helper methods. Therefore we create a custom build using the lodash CLI and bundle only the once we need.
|
|
850
578
|
|
|
851
|
-
|
|
852
|
-
import { string } from '@poppinss/utils/build/helpers'
|
|
579
|
+
> **Why not use something else**: All other helpers I have used are not as accurate or well implemented as lodash.
|
|
853
580
|
|
|
854
|
-
|
|
855
|
-
|
|
581
|
+
- pick
|
|
582
|
+
- omit
|
|
583
|
+
- has
|
|
584
|
+
- get
|
|
585
|
+
- set
|
|
586
|
+
- unset
|
|
587
|
+
- mergeWith
|
|
588
|
+
- merge
|
|
589
|
+
- size
|
|
590
|
+
- clone
|
|
591
|
+
- cloneDeep
|
|
592
|
+
- toPath
|
|
856
593
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
Condense whitespaces from a given string. The method removes the whitespace from the `left`, `right` and multiple whitespace in between the words.
|
|
594
|
+
You can use the methods as follows.
|
|
860
595
|
|
|
861
596
|
```ts
|
|
862
|
-
import
|
|
597
|
+
import lodash from '@poppinss/utils/lodash'
|
|
863
598
|
|
|
864
|
-
|
|
865
|
-
// hello world
|
|
599
|
+
lodash.pick(collection, keys)
|
|
866
600
|
```
|
|
867
601
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
Escape HTML from the string
|
|
602
|
+
### All other helpers
|
|
603
|
+
The following helpers are exported from the package main module.
|
|
871
604
|
|
|
872
605
|
```ts
|
|
873
|
-
import {
|
|
874
|
-
|
|
875
|
-
string.escapeHTML('<p> foo © bar </p>')
|
|
876
|
-
// <p> foo © bar </p>
|
|
606
|
+
import { base64, compose } from '@poppinss/utils'
|
|
877
607
|
```
|
|
878
608
|
|
|
879
|
-
|
|
609
|
+
#### base64
|
|
610
|
+
Utility methods to base64 encode and decode values.
|
|
880
611
|
|
|
881
612
|
```ts
|
|
882
|
-
import {
|
|
613
|
+
import { base64 } from '@poppinss/utils'
|
|
883
614
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
})
|
|
887
|
-
// <p> foo © bar </p>
|
|
615
|
+
base64.encode('hello world')
|
|
616
|
+
// aGVsbG8gd29ybGQ=
|
|
888
617
|
```
|
|
889
618
|
|
|
890
|
-
|
|
619
|
+
Similar to the `encode` method, you can use the `urlEncode` to generate a base64 string safe to pass in a URL.
|
|
891
620
|
|
|
892
|
-
|
|
621
|
+
The `urlEncode` method performs the following replacements.
|
|
893
622
|
|
|
894
|
-
|
|
895
|
-
|
|
623
|
+
- Replace `+` with `-`.
|
|
624
|
+
- Replace `/` with `_`.
|
|
625
|
+
- And remove the `=` sign from the end of the string.
|
|
896
626
|
|
|
897
|
-
|
|
898
|
-
|
|
627
|
+
```ts
|
|
628
|
+
base64.urlEncode('hello world')
|
|
629
|
+
// aGVsbG8gd29ybGQ
|
|
899
630
|
```
|
|
900
631
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
Join an array of words with a separator.
|
|
632
|
+
You can use the `decode` and the `urlDecode` methods to decode a previously encoded base64 string.
|
|
904
633
|
|
|
905
634
|
```ts
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
string.toSentence(['route', 'middleware', 'controller']) // route, middleware, and controller
|
|
635
|
+
base64.decode(base64.encode('hello world'))
|
|
636
|
+
// hello world
|
|
909
637
|
|
|
910
|
-
|
|
638
|
+
base64.urlDecode(base64.urlEncode('hello world'))
|
|
639
|
+
// hello world
|
|
911
640
|
```
|
|
912
641
|
|
|
913
|
-
You can
|
|
914
|
-
|
|
915
|
-
- `separator`: The value between two words except the last one
|
|
916
|
-
- `pairSeparator`: The value between the first and the last word. Used, only when there are two words
|
|
917
|
-
- `lastSeparator`: The value between the second last and the last word. Used, only when there are more than two words
|
|
642
|
+
The `decode` and the `urlDecode` methods return `null` when the input value is an invalid base64 string. You can turn on the `strict` mode to raise an exception instead.
|
|
918
643
|
|
|
919
644
|
```ts
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
lastSeparator: '/or ',
|
|
923
|
-
}) // route/ middleware/or controller
|
|
645
|
+
base64.decode('hello world') // null
|
|
646
|
+
base64.decode('hello world', 'utf-8', true) // raises exception
|
|
924
647
|
```
|
|
925
648
|
|
|
926
|
-
####
|
|
927
|
-
|
|
928
|
-
Convert bytes value to a human readable string. For options, recommend the [bytes](https://www.npmjs.com/package/bytes) package.
|
|
649
|
+
#### compose
|
|
650
|
+
The `compose` helper allows you to use TypeScript class mixins with a cleaner API. Following is an example of mixins usage without the compose helper.
|
|
929
651
|
|
|
930
652
|
```ts
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
653
|
+
class User extends UserWithAttributes(
|
|
654
|
+
UserWithAge(
|
|
655
|
+
UserWithPassword(
|
|
656
|
+
UserWithEmail(BaseModel)
|
|
657
|
+
)
|
|
658
|
+
)
|
|
659
|
+
) {}
|
|
935
660
|
```
|
|
936
661
|
|
|
937
|
-
|
|
662
|
+
Following is an example with the `compose` helper.
|
|
938
663
|
|
|
939
|
-
|
|
664
|
+
- There is no nesting.
|
|
665
|
+
- The order of mixins is from left to right. Whereas earlier, it was inside out.
|
|
940
666
|
|
|
941
667
|
```ts
|
|
942
|
-
import {
|
|
668
|
+
import { compose } from '@poppinss/utils'
|
|
943
669
|
|
|
944
|
-
|
|
670
|
+
class User extends compose(
|
|
671
|
+
BaseModel,
|
|
672
|
+
UserWithEmail,
|
|
673
|
+
UserWithPassword,
|
|
674
|
+
UserWithAge,
|
|
675
|
+
UserWithAttributes
|
|
676
|
+
) {}
|
|
945
677
|
```
|
|
946
678
|
|
|
947
|
-
####
|
|
948
|
-
|
|
949
|
-
Convert time in milliseconds to a human readable string
|
|
950
|
-
|
|
951
|
-
```ts
|
|
952
|
-
import { string } from '@poppinss/utils/build/helpers'
|
|
953
|
-
|
|
954
|
-
string.prettyMs(60000) // 1min
|
|
955
|
-
string.prettyMs(60000, { long: true }) // 1 minute
|
|
956
|
-
```
|
|
679
|
+
#### defineStaticProperty
|
|
680
|
+
The `defineStaticProperty` method allows you to define static properties on a class with different reference strategies.
|
|
957
681
|
|
|
958
|
-
|
|
682
|
+
If you use class inheritance alongside static properties, then either, you will share properties by reference, or you will define them directly on the parent class.
|
|
959
683
|
|
|
960
|
-
|
|
684
|
+
In the following example, we are not inherting `columns` from the `AppModel`. Instead, we define a new set of columns on the `UserModel`.
|
|
961
685
|
|
|
962
686
|
```ts
|
|
963
|
-
|
|
687
|
+
class AppModel {
|
|
688
|
+
static columns = ['id']
|
|
689
|
+
}
|
|
964
690
|
|
|
965
|
-
|
|
691
|
+
class UserModel extends AppModel {
|
|
692
|
+
static columns = ['username']
|
|
693
|
+
}
|
|
966
694
|
```
|
|
967
695
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
Ordinalize a string or a number value
|
|
696
|
+
In the following example, we are inherting `columns` from the `AppModel`. However, the mutations (array.push) from the `UserModel` will reflect on the `AppModel` as well.
|
|
971
697
|
|
|
972
698
|
```ts
|
|
973
|
-
|
|
699
|
+
class AppModel {
|
|
700
|
+
static columns = ['id']
|
|
701
|
+
}
|
|
974
702
|
|
|
975
|
-
|
|
976
|
-
|
|
703
|
+
class UserModel extends AppModel {}
|
|
704
|
+
UserModel.columns.push('username')
|
|
977
705
|
```
|
|
978
706
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
Generate a cryptographically strong random string
|
|
707
|
+
The ideal behavior is to deep clone the `columns` array and then push new values to it.
|
|
982
708
|
|
|
983
709
|
```ts
|
|
984
|
-
import
|
|
710
|
+
import lodash from '@poppinss/utils/lodash'
|
|
985
711
|
|
|
986
|
-
|
|
987
|
-
|
|
712
|
+
class AppModel {
|
|
713
|
+
static columns = ['id']
|
|
714
|
+
}
|
|
988
715
|
|
|
989
|
-
|
|
716
|
+
const inheritedColumns = lodash.cloneDeep(AppModel.columns)
|
|
717
|
+
class UserModel extends AppModel {
|
|
718
|
+
static columns = inheritedColumns.push('username')
|
|
719
|
+
}
|
|
720
|
+
```
|
|
990
721
|
|
|
991
|
-
|
|
722
|
+
The `defineStaticProperty` method abstracts the logic to clone and also performs some interal checks to see if the value is already defined as an `ownProperty` or not.
|
|
992
723
|
|
|
993
724
|
```ts
|
|
994
|
-
|
|
725
|
+
class UserModel extends AppModel {
|
|
726
|
+
}
|
|
995
727
|
|
|
996
|
-
|
|
997
|
-
|
|
728
|
+
defineStaticProperty(UserModel, 'columns', {
|
|
729
|
+
strategy: 'inherit',
|
|
730
|
+
initialValue: [],
|
|
731
|
+
})
|
|
998
732
|
```
|
|
999
733
|
|
|
1000
|
-
|
|
734
|
+
- The `inherit` strategy clones the value from the parent class.
|
|
735
|
+
- The `define` strategy always re-defines the property, discarding any values on the parent class.
|
|
736
|
+
- The `strategy` value can be function to perform a custom clone operations.
|
|
737
|
+
|
|
1001
738
|
|
|
1002
|
-
|
|
739
|
+
#### Exception
|
|
740
|
+
A custom exception class with support for defining the error status, error code, and help description. This class aims to standardize exceptions within your projects.
|
|
1003
741
|
|
|
1004
742
|
```ts
|
|
1005
|
-
|
|
1006
|
-
typeof [] // object
|
|
1007
|
-
typeof null // object
|
|
1008
|
-
```
|
|
743
|
+
import { Exception } from '@poppinss/utils'
|
|
1009
744
|
|
|
1010
|
-
|
|
745
|
+
class ResourceNotFound extends Exception {
|
|
746
|
+
static code = 'E_RESOURCE_NOT_FOUND'
|
|
747
|
+
static status = 404
|
|
748
|
+
static message = 'Unable to find resource'
|
|
749
|
+
}
|
|
1011
750
|
|
|
1012
|
-
|
|
751
|
+
throw new ResourceNotFound()
|
|
752
|
+
```
|
|
1013
753
|
|
|
1014
|
-
|
|
754
|
+
Following is an example of creating a dynamic message with interpolation.
|
|
1015
755
|
|
|
1016
756
|
```ts
|
|
1017
|
-
import {
|
|
757
|
+
import { Exception } from '@poppinss/utils'
|
|
758
|
+
import string from '@poppinss/utils/string'
|
|
1018
759
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
types.lookup(class Foo {}) // class
|
|
1025
|
-
types.lookup(new Map()) // map
|
|
1026
|
-
```
|
|
760
|
+
class ResourceNotFound extends Exception {
|
|
761
|
+
static code = 'E_RESOURCE_NOT_FOUND'
|
|
762
|
+
static status = 404
|
|
763
|
+
static message = 'Unable to find resource with id {{ id }}'
|
|
764
|
+
}
|
|
1027
765
|
|
|
1028
|
-
|
|
766
|
+
throw new ResourceNotFound(
|
|
767
|
+
interpolate(ResourceNotFound.message, { id: 1 })
|
|
768
|
+
)
|
|
769
|
+
```
|
|
1029
770
|
|
|
1030
|
-
|
|
771
|
+
#### flatten
|
|
772
|
+
Create a flat object from a nested object/array. The nested keys are combined with a dot-notation (`.`). The method is exported from the [flattie](https://www.npmjs.com/package/flattie) package.
|
|
1031
773
|
|
|
1032
774
|
```ts
|
|
1033
|
-
import {
|
|
775
|
+
import { flatten } from '@poppinss/utils'
|
|
1034
776
|
|
|
1035
|
-
|
|
1036
|
-
|
|
777
|
+
flatten({
|
|
778
|
+
a: 'hi',
|
|
779
|
+
b: {
|
|
780
|
+
a: null,
|
|
781
|
+
b: ['foo', '', null, 'bar'],
|
|
782
|
+
d: 'hello',
|
|
783
|
+
e: {
|
|
784
|
+
a: 'yo',
|
|
785
|
+
b: undefined,
|
|
786
|
+
c: 'sup',
|
|
787
|
+
d: 0,
|
|
788
|
+
f: [
|
|
789
|
+
{ foo: 123, bar: 123 },
|
|
790
|
+
{ foo: 465, bar: 456 },
|
|
791
|
+
]
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
c: 'world'
|
|
795
|
+
})
|
|
1037
796
|
|
|
1038
|
-
|
|
797
|
+
// {
|
|
798
|
+
// 'a': 'hi',
|
|
799
|
+
// 'b.b.0': 'foo',
|
|
800
|
+
// 'b.b.1': '',
|
|
801
|
+
// 'b.b.3': 'bar',
|
|
802
|
+
// 'b.d': 'hello',
|
|
803
|
+
// 'b.e.a': 'yo',
|
|
804
|
+
// 'b.e.c': 'sup',
|
|
805
|
+
// 'b.e.d': 0,
|
|
806
|
+
// 'b.e.f.0.foo': 123,
|
|
807
|
+
// 'b.e.f.0.bar': 123,
|
|
808
|
+
// 'b.e.f.1.foo': 465,
|
|
809
|
+
// 'b.e.f.1.bar': 456,
|
|
810
|
+
// 'c': 'world'
|
|
811
|
+
// }
|
|
812
|
+
```
|
|
1039
813
|
|
|
1040
|
-
|
|
814
|
+
#### fsReadAll
|
|
815
|
+
Get a list of all the files from a directory. The method recursively fetches files from the main and the sub-folders. The dotfiles are ignored implicitly.
|
|
1041
816
|
|
|
1042
817
|
```ts
|
|
1043
|
-
import {
|
|
818
|
+
import { fsReadAll } from '@poppinss/utils'
|
|
1044
819
|
|
|
1045
|
-
|
|
820
|
+
const files = await fsReadAll(
|
|
821
|
+
new URL('./config', import.meta.url),
|
|
822
|
+
{ pathType: 'url' }
|
|
823
|
+
)
|
|
824
|
+
await Promise.all(files.map((file) => import(file)))
|
|
1046
825
|
```
|
|
1047
826
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
Find if the given value is a buffer
|
|
827
|
+
You can also pass the options along with the directory path as the second argument.
|
|
1051
828
|
|
|
1052
829
|
```ts
|
|
1053
|
-
|
|
830
|
+
type Options = {
|
|
831
|
+
ignoreMissingRoot?: boolean
|
|
832
|
+
filter?: (filePath: string, index: number) => boolean
|
|
833
|
+
sort?: (current: string, next: string) => number
|
|
834
|
+
pathType?: 'relative' | 'unixRelative' | 'absolute' | 'unixAbsolute' | 'url'
|
|
835
|
+
}
|
|
1054
836
|
|
|
1055
|
-
|
|
837
|
+
const options: Partial<Options> = {}
|
|
838
|
+
await fsReadAll(location, options)
|
|
1056
839
|
```
|
|
1057
840
|
|
|
1058
|
-
|
|
841
|
+
| Argument | Type | Description |
|
|
842
|
+
|---------|------|---------------|
|
|
843
|
+
| `ignoreMissingRoot` | boolean | By default, an exception is raised when the root directory is missing. Setting `ignoreMissingRoot` to true will not result in an error and an empty array is returned back. |
|
|
844
|
+
| `filter` | method | Define a filter to ignore certain paths. The method is called on the final list of files. |
|
|
845
|
+
| `sort` | method | Define a custom method to sort file paths. By default, the files are sorted using natural sort. |
|
|
846
|
+
| `pathType` | enum | Define how to return the collected paths. By default, OS-specific relative paths are returned. If you want to import the collected files, you must set the `pathType = 'url'` |
|
|
1059
847
|
|
|
1060
|
-
|
|
848
|
+
#### fsImportAll
|
|
849
|
+
The `fsImportAll` method imports all the files recursively from a given directory and set the exported value from each module on an object.
|
|
1061
850
|
|
|
1062
851
|
```ts
|
|
1063
|
-
import {
|
|
852
|
+
import { fsImportAll } from '@poppinss/utils'
|
|
1064
853
|
|
|
1065
|
-
|
|
854
|
+
const collection = await fsImportAll(new URL('./config', import.meta.url))
|
|
855
|
+
console.log(collection)
|
|
1066
856
|
```
|
|
1067
857
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
```ts
|
|
1073
|
-
import { types } from '@poppinss/utils/build/helpers'
|
|
858
|
+
- Collection is an object with a tree of key-value pair.
|
|
859
|
+
- The key is the nested object created from the file path.
|
|
860
|
+
- Value is the exported values from the module. If a module exports both the `default` and `named` values, then only the default values are used.
|
|
1074
861
|
|
|
1075
|
-
|
|
1076
|
-
```
|
|
862
|
+
The second param is the options to customize the import behavior.
|
|
1077
863
|
|
|
1078
|
-
|
|
864
|
+
| Argument | Type | Description |
|
|
865
|
+
|---------|------|---------------|
|
|
866
|
+
| `ignoreMissingRoot` | boolean | By default, an exception is raised when the root directory is missing. Setting `ignoreMissingRoot` to true will not result in an error and an empty object is returned back. |
|
|
867
|
+
| `filter` | method | Define a filter to ignore certain paths. By default only files ending with `.js`, `.ts`, `.json`, `.cjs`, and `.mjs` are imported. |
|
|
868
|
+
| `sort` | method | Define a custom method to sort file paths. By default, the files are sorted using natural sort. |
|
|
869
|
+
| `transformKeys` | method | Define a callback method to transform the keys for the final object. The method receives an array of nested keys and must return an array back. |
|
|
1079
870
|
|
|
1080
|
-
|
|
871
|
+
#### isScriptFile
|
|
872
|
+
A filter to know if the file path ends with `.js`, `.json`, `.cjs`, `.mjs` or `.ts`. In the case of `.ts` files, the `.d.ts` returns false.
|
|
1081
873
|
|
|
1082
874
|
```ts
|
|
1083
|
-
import {
|
|
875
|
+
import { isScriptFile } from '@poppinss/utils'
|
|
1084
876
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
877
|
+
isScriptFile('foo.js') // true
|
|
878
|
+
isScriptFile('foo/bar.cjs') // true
|
|
879
|
+
isScriptFile('foo/bar.mjs') // true
|
|
880
|
+
isScriptFile('foo.json') // true
|
|
1089
881
|
|
|
1090
|
-
|
|
882
|
+
isScriptFile('foo/bar.ts') // true
|
|
883
|
+
isScriptFile('foo/bar.d.ts') // false
|
|
884
|
+
```
|
|
1091
885
|
|
|
1092
|
-
|
|
886
|
+
The goal of this method is to use it as a filter with the `fsReadAll` method.
|
|
1093
887
|
|
|
1094
888
|
```ts
|
|
1095
|
-
import {
|
|
889
|
+
import { fsReadAll, isScriptFile } from '@poppinss/utils'
|
|
1096
890
|
|
|
1097
|
-
|
|
1098
|
-
|
|
891
|
+
const dir = new URL('./config', import.meta.url)
|
|
892
|
+
const options = { pathType: 'url', filter: isScriptFile }
|
|
1099
893
|
|
|
1100
|
-
|
|
894
|
+
const files = await fsReadAll(dir, options)
|
|
1101
895
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
896
|
+
await Promise.all(files.map((file) => {
|
|
897
|
+
if (file.endsWith('.json')) {
|
|
898
|
+
return import(file, { assert: { type: 'json' } })
|
|
899
|
+
}
|
|
1106
900
|
|
|
1107
|
-
|
|
901
|
+
return import(file)
|
|
902
|
+
}))
|
|
1108
903
|
```
|
|
1109
904
|
|
|
1110
|
-
####
|
|
1111
|
-
|
|
1112
|
-
Find if the given value is an array
|
|
905
|
+
#### naturalSort
|
|
906
|
+
A sorting function to use natural sort for ordering an array.
|
|
1113
907
|
|
|
1114
908
|
```ts
|
|
1115
|
-
import {
|
|
909
|
+
import { naturalSort } from '@poppinss/utils'
|
|
910
|
+
|
|
911
|
+
const values = ['1_foo_bar', '12_foo_bar'].sort()
|
|
912
|
+
// Default sorting: ['12_foo_bar', '1_foo_bar']
|
|
1116
913
|
|
|
1117
|
-
|
|
914
|
+
const values = ['1_foo_bar', '12_foo_bar'].sort(naturalSort)
|
|
915
|
+
// Default sorting: ['1_foo_bar', '12_foo_bar']
|
|
1118
916
|
```
|
|
1119
917
|
|
|
1120
|
-
####
|
|
918
|
+
#### safeEqual
|
|
919
|
+
Check if two buffer or string values are the same. This method does not leak any timing information and prevents [timing attack](https://javascript.plainenglish.io/what-are-timing-attacks-and-how-to-prevent-them-using-nodejs-158cc7e2d70c).
|
|
1121
920
|
|
|
1122
|
-
|
|
921
|
+
Under the hood, this method uses Node.js [crypto.timeSafeEqual](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b) method, with support for comparing string values. *(crypto.timeSafeEqual does not support string comparison)*
|
|
1123
922
|
|
|
1124
923
|
```ts
|
|
1125
|
-
import {
|
|
924
|
+
import { safeEqual } from '@poppinss/utils'
|
|
1126
925
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
926
|
+
/**
|
|
927
|
+
* The trusted value, it might be saved inside the db
|
|
928
|
+
*/
|
|
929
|
+
const trustedValue = 'hello world'
|
|
1131
930
|
|
|
1132
|
-
|
|
931
|
+
/**
|
|
932
|
+
* Untrusted user input
|
|
933
|
+
*/
|
|
934
|
+
const userInput = 'hello'
|
|
1133
935
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
types.isError(new Exception('foo'))) // true
|
|
936
|
+
if (safeEqual(trustedValue, userInput)) {
|
|
937
|
+
// both are same
|
|
938
|
+
} else {
|
|
939
|
+
// value mis-match
|
|
940
|
+
}
|
|
1140
941
|
```
|
|
1141
942
|
|
|
1142
|
-
####
|
|
1143
|
-
|
|
1144
|
-
Find if the given value is a function
|
|
943
|
+
#### slash
|
|
944
|
+
Convert OS-specific file paths to Unix file paths. The method is exported directly from the [slash](https://npm.im/slash) package.
|
|
1145
945
|
|
|
1146
946
|
```ts
|
|
1147
|
-
import {
|
|
1148
|
-
|
|
1149
|
-
types.isFunction(function foo() {})) // true
|
|
947
|
+
import { slash } from '@poppinss/utils'
|
|
948
|
+
slash('foo\\bar') // foo/bar
|
|
1150
949
|
```
|
|
1151
950
|
|
|
1152
|
-
####
|
|
1153
|
-
|
|
1154
|
-
Find if the given value is a class constructor. Uses regex to distinguish between a function and a class.
|
|
951
|
+
#### MessageBuilder
|
|
952
|
+
Message builder is a convenience layer to stringify JavaScript values with an expiry date and a purpose. For example:
|
|
1155
953
|
|
|
1156
954
|
```ts
|
|
1157
|
-
import {
|
|
955
|
+
import { MessageBuilder } from '@poppinss/utils'
|
|
1158
956
|
|
|
1159
|
-
|
|
957
|
+
const builder = new MessageBuilder()
|
|
958
|
+
const encoded = builder.build(
|
|
959
|
+
{
|
|
960
|
+
token: string.random(32),
|
|
961
|
+
},
|
|
962
|
+
'1 hour',
|
|
963
|
+
'email_verification'
|
|
964
|
+
)
|
|
1160
965
|
|
|
1161
|
-
|
|
1162
|
-
|
|
966
|
+
/**
|
|
967
|
+
* {
|
|
968
|
+
* "message": {
|
|
969
|
+
* "token":"GZhbeG5TvgA-7JCg5y4wOBB1qHIRtX6q"
|
|
970
|
+
* },
|
|
971
|
+
* "purpose":"email_verification",
|
|
972
|
+
* "expiryDate":"2022-10-03T04:07:13.860Z"
|
|
973
|
+
* }
|
|
974
|
+
*/
|
|
1163
975
|
```
|
|
1164
976
|
|
|
1165
|
-
|
|
977
|
+
Once you have the JSON string with the expiry and the purpose, you can encrypt it (to prevent tampering) and share it with the client.
|
|
1166
978
|
|
|
1167
|
-
|
|
979
|
+
During the email verification, you can decrypt the key and then ask the `MessageBuilder` to verify the payload.
|
|
1168
980
|
|
|
1169
981
|
```ts
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
types.isInteger(-1) // true
|
|
1175
|
-
types.isInteger(-1.0) // true
|
|
982
|
+
const decoded = builder.verify(value, 'email_verification')
|
|
983
|
+
if (!decoded) {
|
|
984
|
+
return 'Invalid token'
|
|
985
|
+
}
|
|
1176
986
|
|
|
1177
|
-
|
|
1178
|
-
types.isInteger(0.3) // false
|
|
1179
|
-
types.isInteger(-0.3) // false
|
|
987
|
+
console.log(decoded.token)
|
|
1180
988
|
```
|
|
1181
989
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
Find if the given value is an float number.
|
|
990
|
+
Now let's imagine someone presents the same token to reset their account password. In the following example, the validation will fail since the purpose of the original token is not the same as the purpose set during the `verify` method call.
|
|
1185
991
|
|
|
1186
992
|
```ts
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
types.isFloat(22.1) // true
|
|
1190
|
-
types.isFloat(-22.1) // true
|
|
1191
|
-
types.isFloat(0.3) // true
|
|
1192
|
-
types.isFloat(-0.3) // true
|
|
1193
|
-
|
|
1194
|
-
types.isFloat(22.0) // false
|
|
1195
|
-
types.isFloat(-22.0) // false
|
|
1196
|
-
types.isFloat(-22) // false
|
|
993
|
+
const decoded = builder.verify(value, 'reset_password')
|
|
1197
994
|
```
|
|
1198
995
|
|
|
1199
|
-
####
|
|
1200
|
-
|
|
1201
|
-
Find if the given value has a decimal. The value can be a string or a number. The number values are casted to a string by calling the `toString()` method on the value itself.
|
|
1202
|
-
|
|
1203
|
-
The string conversion is peformed to test the value against a regex. Since, there is no way to natively find a decimal value in Javascript.
|
|
996
|
+
#### ObjectBuilder
|
|
997
|
+
The `ObjectBuilder` is a convenience class to create an object with dynamic properties. Consider the following example, where we wrap our code inside conditionals before adding the property `b` to the `startingObject`.
|
|
1204
998
|
|
|
1205
999
|
```ts
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
types.isDecimal('-22.10') // true
|
|
1212
|
-
types.isDecimal(-22.1) // true
|
|
1213
|
-
|
|
1214
|
-
types.isDecimal('.3') // true
|
|
1215
|
-
types.isDecimal(0.3) // true
|
|
1000
|
+
const startingObject = {
|
|
1001
|
+
a: 1
|
|
1002
|
+
// Add "b", if it exists
|
|
1003
|
+
...(b ? { b } : {})
|
|
1004
|
+
}
|
|
1216
1005
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1006
|
+
// OR
|
|
1007
|
+
if (b) {
|
|
1008
|
+
startingObject.b = b
|
|
1009
|
+
}
|
|
1010
|
+
```
|
|
1219
1011
|
|
|
1220
|
-
|
|
1221
|
-
types.isDecimal(22.0) // false (gets converted to 22)
|
|
1012
|
+
Instead of writing conditionals, your can consider using the Object builder fluent API.
|
|
1222
1013
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1014
|
+
```ts
|
|
1015
|
+
const builder = new ObjectBuilder({ a: 1 })
|
|
1225
1016
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1017
|
+
// Add property if the value is not undefined.
|
|
1018
|
+
builder.add('b', b)
|
|
1228
1019
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1020
|
+
// Get the plain object back
|
|
1021
|
+
const plainObject = builder.toObject()
|
|
1231
1022
|
```
|
|
1232
1023
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
A very simple class to conditionally builder an object. Quite often, I create a new object from an existing one and wants to avoid writing undefined values to it. For example
|
|
1024
|
+
By default, only the `undefined` values are ignored. However, you can also ignore `null` values.
|
|
1236
1025
|
|
|
1237
1026
|
```ts
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
...(user.id ? { id: user.id } : {}),
|
|
1241
|
-
...(user.createdAt ? { createdAt: user.createdAt.toString() } : {}),
|
|
1242
|
-
}
|
|
1027
|
+
const ignoreNullValues = true
|
|
1028
|
+
const builder = new ObjectBuilder({ a: 1 }, ignoreNullValues)
|
|
1243
1029
|
```
|
|
1244
1030
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
To address this use case, you can make use of the `ObjectBuilder` class as follows
|
|
1031
|
+
Following are the available methods on the `ObjectBuilder` class.
|
|
1248
1032
|
|
|
1249
1033
|
```ts
|
|
1250
|
-
|
|
1034
|
+
builder.remove(key)
|
|
1035
|
+
builder.has(key)
|
|
1036
|
+
builder.get(key)
|
|
1037
|
+
builder.add(key)
|
|
1251
1038
|
|
|
1252
|
-
|
|
1253
|
-
.add('username', user.username)
|
|
1254
|
-
.add('id', user.id)
|
|
1255
|
-
.add('createdAt', user.createdAt && user.createdAt.toString()).value // returns the underlying object
|
|
1039
|
+
builder.toObject() // get plain object
|
|
1256
1040
|
```
|
|
1257
1041
|
|
|
1258
|
-
|
|
1042
|
+
#### dirname/filename
|
|
1043
|
+
ES modules does not have magic variables `__filename` and `__dirname`. You can use these helpers to get the current directory and filenames as follows.
|
|
1259
1044
|
|
|
1260
1045
|
```ts
|
|
1261
|
-
|
|
1046
|
+
import { getDirname, getFilename } from '@poppinss/utils'
|
|
1047
|
+
|
|
1048
|
+
const dirname = getDirname(import.meta.url)
|
|
1049
|
+
const filename = getFilename(import.meta.url)
|
|
1262
1050
|
```
|
|
1263
1051
|
|
|
1264
1052
|
[gh-workflow-image]: https://img.shields.io/github/workflow/status/poppinss/utils/test?style=for-the-badge
|