Skip to content

Commit

Permalink
Merge pull request #1291 from noinkling/normalize
Browse files Browse the repository at this point in the history
Add string.normalize for unicode normalization
  • Loading branch information
Marsup committed Sep 20, 2017
2 parents 9a8eb5a + a79b59a commit 2adcc09
Show file tree
Hide file tree
Showing 5 changed files with 409 additions and 1 deletion.
16 changes: 16 additions & 0 deletions API.md
Expand Up @@ -125,6 +125,7 @@
- [`string.hex()`](#stringhex)
- [`string.base64([options])`](#stringbase64options)
- [`string.hostname()`](#stringhostname)
- [`string.normalize([form])`](#stringnormalizeform)
- [`string.lowercase()`](#stringlowercase)
- [`string.uppercase()`](#stringuppercase)
- [`string.trim()`](#stringtrim)
Expand Down Expand Up @@ -1910,6 +1911,21 @@ Requires the string value to be a valid hostname as per [RFC1123](http://tools.i
const schema = Joi.string().hostname();
```

#### `string.normalize([form])`

Requires the string value to be in a [unicode normalized](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
form. If the validation `convert` option is on (enabled by default), the string will be normalized.

- `form` - The unicode normalization form to use. Valid values: `NFC` [default], `NFD`, `NFKC`, `NFKD`

```js
const schema = Joi.string().normalize(); // defaults to NFC
const schema = Joi.string().normalize('NFC'); // canonical composition
const schema = Joi.string().normalize('NFD'); // canonical decomposition
const schema = Joi.string().normalize('NFKC'); // compatibility composition
const schema = Joi.string().normalize('NFKD'); // compatibility decomposition
```

#### `string.lowercase()`

Requires the string value to be all lowercase. If the validation `convert` option is on (enabled by default), the string
Expand Down
1 change: 1 addition & 0 deletions lib/language.js
Expand Up @@ -139,6 +139,7 @@ exports.errors = {
hex: 'must only contain hexadecimal characters',
base64: 'must be a valid base64 string',
hostname: 'must be a valid hostname',
normalize: 'must be unicode normalized in the {{form}} form',
lowercase: 'must only contain lowercase characters',
uppercase: 'must only contain uppercase characters',
trim: 'must not have leading or trailing whitespace',
Expand Down
1 change: 1 addition & 0 deletions lib/types/any/index.js
Expand Up @@ -54,6 +54,7 @@ module.exports = internals.Any = class {
encoding: undefined,
insensitive: false,
trim: false,
normalize: undefined, // NFC, NFD, NFKC, NFKD
case: undefined, // upper, lower
empty: undefined,
func: false,
Expand Down
27 changes: 26 additions & 1 deletion lib/types/string/index.js
Expand Up @@ -26,7 +26,8 @@ const internals = {
uuidv4: '4',
uuidv5: '5'
},
cidrPresences: ['required', 'optional', 'forbidden']
cidrPresences: ['required', 'optional', 'forbidden'],
normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
};

internals.String = class extends Any {
Expand All @@ -43,6 +44,10 @@ internals.String = class extends Any {
if (typeof value === 'string' &&
options.convert) {

if (this._flags.normalize) {
value = value.normalize(this._flags.normalize);
}

if (this._flags.case) {
value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase());
}
Expand Down Expand Up @@ -456,6 +461,26 @@ internals.String = class extends Any {
});
}

normalize(form) {

form = form || 'NFC';
Hoek.assert(Hoek.contain(internals.normalizationForms, form), 'normalization form must be one of ' + internals.normalizationForms.join(', '));

const obj = this._test('normalize', form, function (value, state, options) {

if (options.convert ||
value === value.normalize(form)) {

return value;
}

return this.createError('string.normalize', { value, form }, state, options);
});

obj._flags.normalize = form;
return obj;
}

lowercase() {

const obj = this._test('lowercase', undefined, function (value, state, options) {
Expand Down

0 comments on commit 2adcc09

Please sign in to comment.