Skip to content

Commit

Permalink
Add normalize action to normalize strings #691
Browse files Browse the repository at this point in the history
  • Loading branch information
fabian-hiller committed Jul 5, 2024
1 parent 8dcf6c0 commit 555ae78
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 0 deletions.
1 change: 1 addition & 0 deletions library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to the library will be documented in this file.

## vX.X.X (Month DD, YYYY)

- Add `normalize` action to normalize strings (issue #691)
- Add support for async schemas to `entriesFromList` util
- Add support for numbers and symbols to `entriesFromList` util (issue #492)
- Add `key` property to `SetPathItem` type to improve DX (issue #693, #694)
Expand Down
1 change: 1 addition & 0 deletions library/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export * from './minSize/index.ts';
export * from './minValue/index.ts';
export * from './multipleOf/index.ts';
export * from './nonEmpty/index.ts';
export * from './normalize/index.ts';
export * from './notBytes/index.ts';
export * from './notLength/index.ts';
export * from './notSize/index.ts';
Expand Down
1 change: 1 addition & 0 deletions library/src/actions/normalize/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './normalize.ts';
31 changes: 31 additions & 0 deletions library/src/actions/normalize/normalize.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, expectTypeOf, test } from 'vitest';
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts';
import { normalize, type NormalizeAction } from './normalize.ts';

describe('normalize', () => {
describe('should return action object', () => {
test('with undefined form', () => {
expectTypeOf(normalize()).toEqualTypeOf<NormalizeAction<undefined>>();
});

test('with defined form', () => {
expectTypeOf(normalize('NFKC')).toEqualTypeOf<NormalizeAction<'NFKC'>>();
});
});

describe('should infer correct types', () => {
type Action = NormalizeAction<undefined>;

test('of input', () => {
expectTypeOf<InferInput<Action>>().toEqualTypeOf<string>();
});

test('of output', () => {
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<string>();
});

test('of issue', () => {
expectTypeOf<InferIssue<Action>>().toEqualTypeOf<never>();
});
});
});
60 changes: 60 additions & 0 deletions library/src/actions/normalize/normalize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, test } from 'vitest';
import { normalize, type NormalizeAction } from './normalize.ts';

describe('normalize', () => {
describe('should return action object', () => {
const baseAction: Omit<NormalizeAction<never>, 'form'> = {
kind: 'transformation',
type: 'normalize',
reference: normalize,
async: false,
_run: expect.any(Function),
};

test('with undefined form', () => {
expect(normalize()).toStrictEqual({
...baseAction,
form: undefined,
} satisfies NormalizeAction<undefined>);
});

test('with defined form', () => {
expect(normalize('NFKD')).toStrictEqual({
...baseAction,
form: 'NFKD',
} satisfies NormalizeAction<'NFKD'>);
});
});

describe('should normalize string', () => {
test('with undefined form', () => {
expect(
normalize()._run({ typed: true, value: '\u00F1' }, {})
).toStrictEqual({
typed: true,
value: 'ñ',
});
expect(
normalize()._run({ typed: true, value: '\u006E\u0303' }, {})
).toStrictEqual({
typed: true,
value: 'ñ',
});
});

test('with defined form', () => {
expect(
normalize('NFKD')._run({ typed: true, value: '\uFB00' }, {})
).toStrictEqual({
typed: true,
value: 'ff',
});
expect(
normalize('NFKD')._run({ typed: true, value: '\u0066\u0066' }, {})
).toStrictEqual({
typed: true,
value: 'ff',
});
});
});
});
59 changes: 59 additions & 0 deletions library/src/actions/normalize/normalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { BaseTransformation } from '../../types/index.ts';

/**
* Normalize form type.
*/
export type NormalizeForm = 'NFC' | 'NFD' | 'NFKC' | 'NFKD';

/**
* Normalize action type.
*/
export interface NormalizeAction<TForm extends NormalizeForm | undefined>
extends BaseTransformation<string, string, never> {
/**
* The action type.
*/
readonly type: 'normalize';
/**
* The action reference.
*/
readonly reference: typeof normalize;
/**
* The normalization form.
*/
readonly form: TForm;
}

/**
* Creates a normalize transformation action.
*
* @returns A normalize action.
*/
export function normalize(): NormalizeAction<undefined>;

/**
* Creates a normalize transformation action.
*
* @param form The normalization form.
*
* @returns A normalize action.
*/
export function normalize<TForm extends NormalizeForm | undefined>(
form: TForm
): NormalizeAction<TForm>;

export function normalize(
form?: NormalizeForm
): NormalizeAction<NormalizeForm | undefined> {
return {
kind: 'transformation',
type: 'normalize',
reference: normalize,
async: false,
form,
_run(dataset) {
dataset.value = dataset.value.normalize(this.form);
return dataset;
},
};
}
58 changes: 58 additions & 0 deletions website/src/routes/api/(actions)/normalize/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: normalize
description: Creates a normalize transformation action.
source: /actions/normalize/normalize.ts
contributors:
- fabian-hiller
---

import { ApiList, Property } from '~/components';
import { properties } from './properties';

# normalize

Creates a normalize transformation action.

```ts
const Action = v.normalize<TForm>(form);
```

## Generics

- `TForm` <Property {...properties.TForm} />

## Parameters

- `form` <Property {...properties.form} />

## Returns

- `Action` <Property {...properties.Action} />

## Examples

The following examples show how `normalize` can be used.

### Normalized string

Schema to normalize a string.

```ts
const StringSchema = v.pipe(v.string(), v.normalize());
```

## Related

The following APIs can be combined with `normalize`.

### Schemas

<ApiList items={['any', 'special', 'string', 'unknown']} />

### Methods

<ApiList items={['pipe']} />

### Utils

<ApiList items={['isOfKind', 'isOfType']} />
31 changes: 31 additions & 0 deletions website/src/routes/api/(actions)/normalize/properties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { PropertyProps } from '~/components';

export const properties: Record<string, PropertyProps> = {
TForm: {
modifier: 'extends',
type: {
type: 'union',
options: [
{
type: 'custom',
name: 'NormalizeForm',
href: '../NormalizeForm/',
},
'undefined',
],
},
},
form: {
type: {
type: 'custom',
name: 'TForm',
},
},
Action: {
type: {
type: 'custom',
name: 'NormalizeAction',
href: '../NormalizeAction/',
},
},
};
1 change: 1 addition & 0 deletions website/src/routes/api/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
- [minValue](/api/minValue/)
- [multipleOf](/api/multipleOf/)
- [nonEmpty](/api/nonEmpty/)
- [normalize](/api/normalize/)
- [notBytes](/api/notBytes/)
- [notLength](/api/notLength/)
- [notSize](/api/notSize/)
Expand Down

0 comments on commit 555ae78

Please sign in to comment.