From 13c162bf76ec092b4a4bcf89cf291382fa338070 Mon Sep 17 00:00:00 2001
From: Arjun Vegda <14841132+arjunvegda@users.noreply.github.com>
Date: Sat, 19 Feb 2022 22:37:41 -0500
Subject: [PATCH 01/13] feat: add full width characters (#36)
* perf: improve regex perf. by fixing catastrophic backtracking
* test: polish tests for kana/kanji fns
* feat: add full width characters
* docs(readme.md): add link to the website containing demos
---
README.md | 22 +++++++++--
__tests__/constants.ts | 42 ++++++++++++++++++++
__tests__/utils.ts | 27 +++++++++++--
__tests__/validators/japanese.test.ts | 29 +++++++++-----
__tests__/validators/kana.test.ts | 11 +++---
__tests__/validators/kanji.test.ts | 3 +-
src/constants/built-ranges.ts | 18 +++++++++
src/constants/index.ts | 8 ++++
src/constants/raw-ranges.ts | 56 +++++++++++++++++++++++++++
src/types.ts | 6 ++-
src/utils/create-range.ts | 2 +-
src/utils/create-regex-groups.ts | 24 ++++++------
src/utils/create-strict-validator.ts | 2 +-
src/utils/create-unicode.ts | 3 +-
src/validators/japanese.ts | 4 ++
15 files changed, 218 insertions(+), 39 deletions(-)
diff --git a/README.md b/README.md
index 932f71e..880ceb2 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
-
+
+
+
@@ -21,7 +23,7 @@
The word "Moji" translates to "Character" in English
-## 🚀 Features
+## ✨ Features
- Very small footprint with zero dependencies
- Supports strict and threshold-based validation for Kanji, Kana, and all the Japanese characters
@@ -31,6 +33,11 @@ The word "Moji" translates to "Character" in English
- Supports custom single Unicode and Unicode ranges out-of-the-box
- 100% TypeScript friendly
- ESM, CJS, and UMD builds
+- Only pay for what you use this library is fully tree-shakable
+
+## 🚀 Demo
+
+Live demos are on this [website](https://japanese-moji.vercel.app)
## 📦 Install
@@ -85,7 +92,8 @@ const howMuchKanaIsPresentResult = howMuchKanaIsPresent('some string here');
### Japanese
Validates CJK punctuations, Hiragana, Katakana, Katakana phonetic extensions, Rare Kanji, Common and
-Uncommon Kanji, Kanji compatibility ideographs, and Half-width Katakana forms
+Uncommon Kanji, Kanji compatibility ideographs, Half-width Katakana forms, and Full-width (roman)
+forms including punctuations
```ts
import { isValidJapanese, isJapanesePresent, howMuchJapaneseIsPresent } from 'japanese-moji';
@@ -112,6 +120,10 @@ enum CharacterSet {
KanjiCompatibilityIdeographs = 'KanjiCompatibilityIdeographs',
CommonUncommonKanji = 'CommonUncommonKanji',
HalfWidthKatakana = 'HalfWidthKatakana',
+ FullWidthUpperCase = 'FullWidthUpperCase',
+ FullWidthLowerCase = 'FullWidthLowerCase',
+ FullWidthNumbers = 'FullWidthNumbers',
+ FullWidthPunctuations = 'FullWidthPunctuations',
}
interface UnicodeRange {
@@ -172,6 +184,10 @@ const options: CreateValidatorOptions = {
CharacterSet.CommonUncommonKanji,
CharacterSet.KanjiCompatibilityIdeographs,
CharacterSet.HalfWidthKatakana,
+ CharacterSet.FullWidthUpperCase,
+ CharacterSet.FullWidthLowerCase,
+ CharacterSet.FullWidthNumbers,
+ CharacterSet.FullWidthPunctuations,
],
customRanges, // Optional
customUnicodes, // Optional
diff --git a/__tests__/constants.ts b/__tests__/constants.ts
index 2f19b12..2466586 100644
--- a/__tests__/constants.ts
+++ b/__tests__/constants.ts
@@ -39,3 +39,45 @@ export const MockHalfWidthKatakana: UnicodeRange = {
start: '\uff66',
end: '\uff9f',
};
+
+export const MockFullWidthUpperCaseRange: UnicodeRange = {
+ start: '\uff21',
+ end: '\uff3a',
+};
+
+export const MockFullWidthLowerCaseRange: UnicodeRange = {
+ start: '\uff41',
+ end: '\uff5a',
+};
+
+export const MockFullWidthNumbersRange: UnicodeRange = {
+ start: '\uff10',
+ end: '\uff19',
+};
+
+export const MockFullWidthPunctuations: UnicodeRange = {
+ start: '\uff00',
+ end: '\uff0f',
+};
+
+const MockFullWidthPunctuationsExtensionA: UnicodeRange = {
+ start: '\uff1a',
+ end: '\uff20',
+};
+
+const MockFullWidthPunctuationsExtensionB: UnicodeRange = {
+ start: '\uff3b',
+ end: '\uff40',
+};
+
+const MockFullWidthPunctuationsExtensionC: UnicodeRange = {
+ start: '\uff5b',
+ end: '\uff65',
+};
+
+export const MockFullWidthPunctuationsRange: UnicodeRange[] = [
+ MockFullWidthPunctuations,
+ MockFullWidthPunctuationsExtensionA,
+ MockFullWidthPunctuationsExtensionB,
+ MockFullWidthPunctuationsExtensionC,
+];
diff --git a/__tests__/utils.ts b/__tests__/utils.ts
index 7ace25c..59a28e8 100644
--- a/__tests__/utils.ts
+++ b/__tests__/utils.ts
@@ -1,16 +1,35 @@
import type { UnicodeRange } from '../src';
import { makeString } from '../src/utils';
-export const generateCharactersFromRange = (start: string, end: string): string => {
+// Useful when string is huge and we want to test both ends of the string
+// For example, test abc...xyz instead of the full abcdef... string, Since we only care about the start/end
+const truncateMiddle = (str: string, width: number) => {
+ if (str.length <= width) {
+ return str;
+ }
+
+ const start = Math.ceil(width / 2);
+ const end = Math.floor(width / 2);
+
+ return str.substring(0, start) + str.substring(str.length - end);
+};
+
+export const generateCharactersFromRange = (start: string, end: string, width?: number): string => {
const startCode = start.charCodeAt(0);
const endCode = end.charCodeAt(0);
const length = endCode - startCode + 1;
- return Array.from({ length }, (_, i) => String.fromCharCode(startCode + i)).join('');
+ const fullStr = Array.from({ length }, (_, i) => String.fromCharCode(startCode + i)).join('');
+
+ if (typeof width === 'number') {
+ return truncateMiddle(fullStr, width);
+ }
+
+ return fullStr;
};
-export const generateCharactersFromRanges = (ranges: UnicodeRange[]): string => {
+export const generateCharactersFromRanges = (ranges: UnicodeRange[], width?: number): string => {
return ranges.reduce((acc, range) => {
- return acc + generateCharactersFromRange(range.start, range.end);
+ return acc + generateCharactersFromRange(range.start, range.end, width);
}, '');
};
diff --git a/__tests__/validators/japanese.test.ts b/__tests__/validators/japanese.test.ts
index 64bda89..59997a0 100644
--- a/__tests__/validators/japanese.test.ts
+++ b/__tests__/validators/japanese.test.ts
@@ -9,6 +9,10 @@ import {
MockKatakanaRange,
MockRareKanjiRange,
MockHalfWidthKatakana,
+ MockFullWidthUpperCaseRange,
+ MockFullWidthLowerCaseRange,
+ MockFullWidthNumbersRange,
+ MockFullWidthPunctuationsRange,
} from '../constants';
describe('validator - Japanese', () => {
@@ -21,24 +25,28 @@ describe('validator - Japanese', () => {
MockHiraganaRange,
MockCJKPunctuationsRange,
MockHalfWidthKatakana,
+ MockFullWidthUpperCaseRange,
+ MockFullWidthLowerCaseRange,
+ MockFullWidthNumbersRange,
+ ...MockFullWidthPunctuationsRange,
]);
const invalidString = getRandomString(500);
describe('isValidJapanese', () => {
- test('should return true when only Kanji characters are supplied', () => {
+ test('should return true when only Japanese characters are supplied', () => {
const result = isValidJapanese(fullJapaneseString);
expect(result).toBe(true);
});
- test('should return false when only Kanji and non Kanji characters are supplied', () => {
+ test('should return false when only Japanese and non Japanese characters are supplied', () => {
const result = isValidJapanese(fullJapaneseString + invalidString);
expect(result).toBe(false);
});
});
describe('isValidJapanesePresent', () => {
- test('should return true when only Kanji characters are supplied', () => {
+ test('should return true when only Japanese characters are supplied', () => {
const result = isJapanesePresent(fullJapaneseString);
expect(result).toBe(true);
});
@@ -54,19 +62,20 @@ describe('validator - Japanese', () => {
});
});
- describe('howMuchKanjiPresent', () => {
- test('should return 100 when only Kanji characters are supplied', () => {
+ describe('howMuchJapanesePresent', () => {
+ test('should return 100 when only Japanese characters are supplied', () => {
const result = howMuchJapaneseIsPresent(fullJapaneseString);
expect(result).toBe(100);
});
- test('should return 98 when only Kanji characters are supplied', () => {
- const result = +howMuchJapaneseIsPresent(invalidString + fullJapaneseString);
- expect(toFixedNumber(result)).toBe(98);
+ test('should return 98 when only Japanese characters are supplied', () => {
+ const result = howMuchJapaneseIsPresent(invalidString + fullJapaneseString);
+ // Precision is important here since the length of the string is huge
+ expect(toFixedNumber(result, 3)).toBe(98.276);
});
- test('should return 0 when no Kanji characters are supplied', () => {
- const result = +howMuchJapaneseIsPresent(invalidString);
+ test('should return 0 when no Japanese characters are supplied', () => {
+ const result = howMuchJapaneseIsPresent(invalidString);
expect(toFixedNumber(result)).toBe(0);
});
});
diff --git a/__tests__/validators/kana.test.ts b/__tests__/validators/kana.test.ts
index a6daf02..abd5a63 100644
--- a/__tests__/validators/kana.test.ts
+++ b/__tests__/validators/kana.test.ts
@@ -1,4 +1,4 @@
-import { generateCharactersFromRanges, toFixedNumber } from '../utils';
+import { generateCharactersFromRanges, getRandomString, toFixedNumber } from '../utils';
import { howMuchKanaIsPresent, isKanaPresent, isValidKana } from '../../src';
import {
MockHalfWidthKatakana,
@@ -13,7 +13,7 @@ describe('validator - kana', () => {
MockHalfWidthKatakana,
]);
- const invalidString = 'abcdefghijklmnopqrstuvwxyz';
+ const invalidString = getRandomString(27);
describe('isValidKana', () => {
test('should return true when only kana characters are supplied', () => {
@@ -51,12 +51,13 @@ describe('validator - kana', () => {
});
test('should return 89 when only kana characters are supplied', () => {
- const result = +howMuchKanaIsPresent(fullKanaString + invalidString);
- expect(toFixedNumber(result)).toBe(87);
+ const result = howMuchKanaIsPresent(fullKanaString + invalidString);
+ // Precision is important here due to the length of the string
+ expect(toFixedNumber(result, 3)).toBe(86.294);
});
test('should return 0 when no kana characters are supplied', () => {
- const result = +howMuchKanaIsPresent(invalidString);
+ const result = howMuchKanaIsPresent(invalidString);
expect(toFixedNumber(result)).toBe(0);
});
});
diff --git a/__tests__/validators/kanji.test.ts b/__tests__/validators/kanji.test.ts
index ee71fbf..de8f6cf 100644
--- a/__tests__/validators/kanji.test.ts
+++ b/__tests__/validators/kanji.test.ts
@@ -62,7 +62,8 @@ describe('validator - Kanji', () => {
test('should return 96 when only Kanji characters are supplied', () => {
const result = howMuchKanjiIsPresent(invalidString + rareKanjiRange);
- expect(toFixedNumber(result)).toBe(96);
+ // Precision is important here due to the length of the string
+ expect(toFixedNumber(result, 3)).toBe(95.647);
});
test('should return 0 when no Kanji characters are supplied', () => {
diff --git a/src/constants/built-ranges.ts b/src/constants/built-ranges.ts
index 9358c8e..1c36196 100644
--- a/src/constants/built-ranges.ts
+++ b/src/constants/built-ranges.ts
@@ -7,8 +7,13 @@ import {
KatakanaRange,
RareKanjiRange,
KanjiCompatibilityIdeographsRange,
+ FullWidthPunctuationsRange,
+ FullWidthUpperCaseRange,
+ FullWidthNumbersRange,
+ FullWidthLowerCaseRange,
} from './raw-ranges';
import { createRange } from '../utils/create-range';
+import { createRanges } from '../utils/create-ranges';
export const CJKPunctuations = createRange(CJKPunctuationsRange.start, CJKPunctuationsRange.end);
export const Hiragana = createRange(HiraganaRange.start, HiraganaRange.end);
@@ -32,3 +37,16 @@ export const HalfWidthKatakana = createRange(
HalfWidthKatakanaRange.start,
HalfWidthKatakanaRange.end,
);
+
+export const FullWidthUpperCase = createRange(
+ FullWidthUpperCaseRange.start,
+ FullWidthUpperCaseRange.end,
+);
+
+export const FullWidthLowerCase = createRange(
+ FullWidthLowerCaseRange.start,
+ FullWidthLowerCaseRange.end,
+);
+
+export const FullWidthNumbers = createRange(FullWidthNumbersRange.start, FullWidthNumbersRange.end);
+export const FullWidthPunctuations = createRanges(FullWidthPunctuationsRange);
diff --git a/src/constants/index.ts b/src/constants/index.ts
index 737ea9f..de3a9cc 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -8,6 +8,10 @@ import {
KatakanaPhoneticExtension,
RareKanji,
KanjiCompatibilityIdeographs,
+ FullWidthPunctuations,
+ FullWidthLowerCase,
+ FullWidthUpperCase,
+ FullWidthNumbers,
} from './built-ranges';
export const characterSetMap: Readonly = Object.freeze({
@@ -19,6 +23,10 @@ export const characterSetMap: Readonly = Object.freeze({
[CharacterSet.KanjiCompatibilityIdeographs]: KanjiCompatibilityIdeographs,
[CharacterSet.CommonUncommonKanji]: CommonUncommonKanji,
[CharacterSet.HalfWidthKatakana]: HalfWidthKatakana,
+ [CharacterSet.FullWidthUpperCase]: FullWidthUpperCase,
+ [CharacterSet.FullWidthLowerCase]: FullWidthLowerCase,
+ [CharacterSet.FullWidthNumbers]: FullWidthNumbers,
+ [CharacterSet.FullWidthPunctuations]: FullWidthPunctuations,
});
export const defaultValidationThreshold = 85;
diff --git a/src/constants/raw-ranges.ts b/src/constants/raw-ranges.ts
index f6770a0..026c08e 100644
--- a/src/constants/raw-ranges.ts
+++ b/src/constants/raw-ranges.ts
@@ -42,3 +42,59 @@ export const HalfWidthKatakanaRange: UnicodeRange = {
start: '\\uff66',
end: '\\uff9f',
};
+
+// Roman full width forms
+// Example - ABCD...Z
+export const FullWidthUpperCaseRange: UnicodeRange = {
+ start: '\\uff21',
+ end: '\\uff3a',
+};
+
+// Roman full width forms
+// Example - abcd...z
+export const FullWidthLowerCaseRange: UnicodeRange = {
+ start: '\\uff41',
+ end: '\\uff5a',
+};
+
+// Roman half width forms
+// Example - 0123...9
+export const FullWidthNumbersRange: UnicodeRange = {
+ start: '\\uff10',
+ end: '\\uff19',
+};
+
+// Roman full width forms
+// List - !"#$%&'()*+,-./
+export const FullWidthPunctuations: UnicodeRange = {
+ start: '\\uff00',
+ end: '\\uff0f',
+};
+
+// Roman full width forms
+// List - :;<=>?@
+const FullWidthPunctuationsExtensionA: UnicodeRange = {
+ start: '\\uff1a',
+ end: '\\uff20',
+};
+
+// Roman full width forms
+// List -[\]^_`
+const FullWidthPunctuationsExtensionB: UnicodeRange = {
+ start: '\\uff3b',
+ end: '\\uff40',
+};
+
+// Roman full width forms
+// List -{|}~⦅⦆。「」、・
+const FullWidthPunctuationsExtensionC: UnicodeRange = {
+ start: '\\uff5b',
+ end: '\\uff65',
+};
+
+export const FullWidthPunctuationsRange: UnicodeRange[] = [
+ FullWidthPunctuations,
+ FullWidthPunctuationsExtensionA,
+ FullWidthPunctuationsExtensionB,
+ FullWidthPunctuationsExtensionC,
+];
diff --git a/src/types.ts b/src/types.ts
index 121a187..12a3203 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -7,9 +7,13 @@ export enum CharacterSet {
KanjiCompatibilityIdeographs = 'KanjiCompatibilityIdeographs',
CommonUncommonKanji = 'CommonUncommonKanji',
HalfWidthKatakana = 'HalfWidthKatakana',
+ FullWidthUpperCase = 'FullWidthUpperCase',
+ FullWidthLowerCase = 'FullWidthLowerCase',
+ FullWidthNumbers = 'FullWidthNumbers',
+ FullWidthPunctuations = 'FullWidthPunctuations',
}
-export type CharacterDict = Record;
+export type CharacterDict = Record;
/**
* All the values must be escaped to be used in a regex
diff --git a/src/utils/create-range.ts b/src/utils/create-range.ts
index 5ac7b3f..f991b73 100644
--- a/src/utils/create-range.ts
+++ b/src/utils/create-range.ts
@@ -17,5 +17,5 @@ export const createRange = (start: string, end: string): string => {
return '';
}
- return makeString('[', start, '-', end, ']');
+ return makeString(start, '-', end);
};
diff --git a/src/utils/create-regex-groups.ts b/src/utils/create-regex-groups.ts
index e47c601..e255d17 100644
--- a/src/utils/create-regex-groups.ts
+++ b/src/utils/create-regex-groups.ts
@@ -6,19 +6,21 @@ export const createRegexGroups = (
characterSets: CharacterSet[],
customRanges: string[],
) => {
- const mappedStrings = characterSets.map((key: CharacterSet) => {
+ const mappedStrings: string[] = [];
+ for (let i = 0; i < characterSets.length; i++) {
+ const key = characterSets[i];
const foundSet = characterSetMap[key];
- if (!foundSet) {
- return '';
+ if (foundSet) {
+ if (Array.isArray(foundSet)) {
+ mappedStrings.push(...foundSet);
+ } else {
+ mappedStrings.push(foundSet);
+ }
}
+ }
- return makeString(foundSet);
- });
-
- const finalCharacterRanges = mappedStrings.filter(Boolean);
- finalCharacterRanges.push(...customRanges);
- const joinByOr = finalCharacterRanges.join('|');
-
- return makeString('(', joinByOr, ')');
+ mappedStrings.push(...customRanges);
+ const joinStr = mappedStrings.join('');
+ return makeString('[', joinStr, ']');
};
diff --git a/src/utils/create-strict-validator.ts b/src/utils/create-strict-validator.ts
index 0ff2993..d2880cd 100644
--- a/src/utils/create-strict-validator.ts
+++ b/src/utils/create-strict-validator.ts
@@ -15,7 +15,7 @@ export const createStrictValidator = (options: CreateValidatorOptions): StrictVa
const finalRegexPattern = makeString('^', regexGroup, '+$');
const customValidator = (str: string): boolean => {
- const regexExp = new RegExp(finalRegexPattern, 'i');
+ const regexExp = new RegExp(finalRegexPattern, 'ui');
return regexExp.test(str);
};
diff --git a/src/utils/create-unicode.ts b/src/utils/create-unicode.ts
index 50be38c..3119c93 100644
--- a/src/utils/create-unicode.ts
+++ b/src/utils/create-unicode.ts
@@ -1,5 +1,4 @@
import { isValidUnicode } from './is-valid-unicode';
-import { makeString } from './make-string';
export const createUnicode = (str: string): string => {
const isValidCode = isValidUnicode(str);
@@ -8,5 +7,5 @@ export const createUnicode = (str: string): string => {
return '';
}
- return makeString('[', str, ']');
+ return str;
};
diff --git a/src/validators/japanese.ts b/src/validators/japanese.ts
index ab1f0be..7e185ef 100644
--- a/src/validators/japanese.ts
+++ b/src/validators/japanese.ts
@@ -15,6 +15,10 @@ const options: CreateValidatorOptions = {
CharacterSet.CommonUncommonKanji,
CharacterSet.KanjiCompatibilityIdeographs,
CharacterSet.HalfWidthKatakana,
+ CharacterSet.FullWidthUpperCase,
+ CharacterSet.FullWidthLowerCase,
+ CharacterSet.FullWidthNumbers,
+ CharacterSet.FullWidthPunctuations,
],
};
From a69b92247408c47a65d9c756f27d552f43e5421d Mon Sep 17 00:00:00 2001
From: semantic-release-bot
Date: Sun, 20 Feb 2022 03:39:20 +0000
Subject: [PATCH 02/13] chore(release): 1.1.0-next.1 [skip ci]
# [1.1.0-next.1](https://github.com/arjunvegda/japanese-moji/compare/v1.0.0...v1.1.0-next.1) (2022-02-20)
### Features
* add full width characters ([#36](https://github.com/arjunvegda/japanese-moji/issues/36)) ([13c162b](https://github.com/arjunvegda/japanese-moji/commit/13c162bf76ec092b4a4bcf89cf291382fa338070))
---
docs/CHANGELOG.md | 7 +++++++
package.json | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 8aae20b..5c9ce80 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,3 +1,10 @@
+# [1.1.0-next.1](https://github.com/arjunvegda/japanese-moji/compare/v1.0.0...v1.1.0-next.1) (2022-02-20)
+
+
+### Features
+
+* add full width characters ([#36](https://github.com/arjunvegda/japanese-moji/issues/36)) ([13c162b](https://github.com/arjunvegda/japanese-moji/commit/13c162bf76ec092b4a4bcf89cf291382fa338070))
+
# 1.0.0 (2022-02-12)
diff --git a/package.json b/package.json
index 336fecc..ef364ef 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "japanese-moji",
- "version": "1.0.0",
+ "version": "1.1.0-next.1",
"description": "A toolkit to validate Japanese characters",
"type": "module",
"packageManager": "yarn@3.1.1",
From 27fc57d402e1aa57e8c0b12cff67db1e2c7eafeb Mon Sep 17 00:00:00 2001
From: Arjun Vegda <14841132+arjunvegda@users.noreply.github.com>
Date: Sun, 20 Feb 2022 15:17:00 -0500
Subject: [PATCH 03/13] docs: add recipes, contribution guide and TOCs (#37)
* docs: add recipes, contribution guide and TOCs
* chore(package.json): mark package as no side effects
* docs(CONTRIBUTING.md): update size of release diagram
* docs(readme.md): add types summary to TOC
* docs(readme.md): add treeshake support badge
---
CONTRIBUTING.md | 143 ++++++++++++++++++++++++++++++++++++++++
README.md | 36 ++++++++--
docs/RECIPES.md | 150 ++++++++++++++++++++++++++++++++++++++++++
docs/release-flow.gif | Bin 0 -> 646792 bytes
package.json | 1 +
5 files changed, 326 insertions(+), 4 deletions(-)
create mode 100644 CONTRIBUTING.md
create mode 100644 docs/RECIPES.md
create mode 100644 docs/release-flow.gif
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..d4bd61e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,143 @@
+# Contributing to Japanese Moji
+
+We are open to, and grateful for, any contributions made to this repository by you. Please review
+the following guide to keep track of our best practices
+
+**Table of Contents**
+
+- [Reporting Issues](#reporting-issues)
+- [Local setup](#local-setup)
+- [Building](#building)
+- [Testing and Linting](#testing-and-linting)
+- [New Features](#new-features)
+- [Submitting Changes](#submitting-changes)
+ - [Making a Pull Request?](#making-a-pull-request)
+ - [Commit Convention](#commit-convention)
+- [Release flow](#release-flow)
+
+## Reporting Issues
+
+Before opening an issue, please search the
+[issue tracker](https://github.com/arjunvegda/japanese-moji/issues) to make sure your issue hasn't
+already been reported.
+
+Please ask any general and implementation specific questions via
+[GitHub Discussion](https://github.com/arjunvegda/japanese-moji/discussions)
+
+## Local setup
+
+Visit the [Issue tracker](https://github.com/arjunvegda/japanese-moji/issues) to find a list of open
+issues that need attention.
+
+Fork, then clone the repo:
+
+```
+git clone https://github.com/your-username/japanese-moji.git
+```
+
+This repository uses Yarn v3 to manage this pacakage and documentations. You'll need to have Yarn
+v1.22 installed globally on your system first, as Yarn v3 depends on that being available first.
+Install dependencies with:
+
+```
+yarn install
+
+# if you want to work on the documentation locally
+cd website && yarn install
+```
+
+## Building
+
+Running the `build` task will create CommonJS, ESM, and a UMD build under `/dist`
+
+```
+yarn build
+```
+
+## Testing and Linting
+
+We use Jest to test our code. Therefore, yarn run test\* supports passing of all the arguments as
+Jest
+
+To run the tests:
+
+```
+yarn test
+```
+
+To continuously watch and run tests, run the following (supports all the arguments as jest):
+
+```
+yarn test:watch
+```
+
+To run tests with coverage, run the following (supports all the arguments as jest):
+
+```
+yarn test:coverage
+```
+
+To perform linting with `eslint`, run the following:
+
+```
+yarn lint
+```
+
+To perform linting with `eslint` and fix it, run the following:
+
+```
+yarn lint:fix
+```
+
+## New Features
+
+Please open an issue with a proposal for a new feature or refactoring before starting on the work.
+We don't want you to waste your efforts on a pull request that we won't want to accept.
+
+## Submitting Changes
+
+- Open a new issue in the [Issue tracker](https://github.com/arjunvegda/japanese-moji/issues)
+- Fork the repo
+- Create a new feature branch based off the `next` branch
+- Make sure all tests pass and that there are no linting errors
+- Submit a pull request, referencing any issues it addresses
+
+### Making a Pull Request?
+
+Pull requests require an approval from the maintainers of the project.
+
+#### Commit Convention
+
+We use
+[Angular's Conventional commit conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type).
+Please use `category(scope or module): message` format in your commit message and use the following
+categories
+
+🚨 **All the changes under `./website/*` are categorized as `docs`, please use `docs` as category
+for your commit message in those cases**
+
+- **build**: Changes that affect the build system or external dependencies (example scopes: npm,
+ yarn)
+- **ci**: Changes to our CI configuration files and scripts (examples: GitHub Actions)
+- **docs**: Documentation only changes
+- **feat**: A new feature
+- **fix**: A bug fix
+- **perf**: A code change that improves performance
+- **refactor**: A code change that neither fixes a bug nor adds a feature
+- **test**: Adding missing tests or correcting existing tests
+- **chore**: All changes other changes to the repository that do not fit into any of the above
+ categories
+
+Please try to keep your pull request focused in scope and avoid including unrelated commits.
+
+After you have submitted your pull request, we'll try to get back to you as soon as possible. We may
+suggest some changes or improvements.
+
+Thank you for contributing 🙌!
+
+## Release flow
+
+We automatically release new versions of `next` branch to next channel on each merge. We also
+release new versions of `main` branch on the stable channel on each merge.
+
+
diff --git a/README.md b/README.md
index 880ceb2..03ea90e 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
-
+
@@ -13,6 +13,9 @@
+
+
+
@@ -23,6 +26,21 @@
The word "Moji" translates to "Character" in English
+**Table of Contents**
+
+- [Features](#-features)
+- [Demo](#-demo)
+- [Install](#-install)
+- [Quick start](#-quick-start)
+ - [Kanji](#kanji)
+ - [Kana](#kana)
+ - [Japanese](#japanese)
+- [Types summary](#-types-summary)
+- [Build Your Own Validators](#-build-your-own-validators)
+- [Recipes](#-recipes)
+- [Contributing](#-contributing)
+- [Changelog](#-changelog)
+
## ✨ Features
- Very small footprint with zero dependencies
@@ -33,7 +51,7 @@ The word "Moji" translates to "Character" in English
- Supports custom single Unicode and Unicode ranges out-of-the-box
- 100% TypeScript friendly
- ESM, CJS, and UMD builds
-- Only pay for what you use this library is fully tree-shakable
+- Only pay for what you use — its fully tree-shakable
## 🚀 Demo
@@ -108,7 +126,7 @@ const isJapanesePresentResult = isJapanesePresent('some string here', 90);
const howMuchJapaneseIsPresentResult = howMuchJapaneseIsPresent('some string here');
```
-### 📝 Types summary
+## 📝 Types summary
```ts
enum CharacterSet {
@@ -207,6 +225,16 @@ const isCustomPresentResult = isCustomPresent('some string here', 90);
const howMuchIsCustomPresentResult = howMuchIsCustomPresent('some string here');
```
-### 📄 Changelog
+## 📚 Recipes
+
+Find code snippets containing variety of combinations that makes creating custom validators easy in
+[docs/RECIPES.md](./docs/RECIPES.md)
+
+## 🙌 Contributing
+
+Find contribution guides, code convention, release flows etc. in
+[CONTRIBUTING.md](./CONTRIBUTING.md)
+
+## 📄 Changelog
Full change log available in the [docs/CHANGELOG.md](./docs/CHANGELOG.md)
diff --git a/docs/RECIPES.md b/docs/RECIPES.md
new file mode 100644
index 0000000..87fdeda
--- /dev/null
+++ b/docs/RECIPES.md
@@ -0,0 +1,150 @@
+# 📚 Recipes
+
+A collection of code snippets to make creating
+[custom validators](../README.md#-build-your-own-validators) easy. These recipes could be supplied
+to create custom validators. [Kana](../README.md#kana), [Kanji](../README.md#kanji) and
+[Japanese](../README.md#japanese) character validators are available out-of-the-box. Check out
+[README.md](../README.md) for more details.
+
+## Template
+
+Copy/paste this template and replace "options" with your choice of set from below
+
+```ts
+import {
+ // Types
+ UnicodeRange,
+ CreateValidatorOptions,
+ StrictValidator,
+ ThresholdBasedValidator,
+ MatchScoreCalculator,
+ CharacterSet,
+
+ // Functions
+ createStrictValidator,
+ createThresholdBasedValidator,
+ createMatchScoreCalculator,
+} from 'japanese-moji';
+
+const options: CreateValidatorOptions = {
+ // Required property
+ characterSets: [
+ // Mix/Match character sets here
+ ],
+ // Optional properties
+ // customRanges?: UnicodeRange[];
+ // customUnicodes?: string[];
+};
+
+const isValidCustom: StrictValidator = createStrictValidator(options);
+const isCustomPresent: ThresholdBasedValidator = createThresholdBasedValidator(options);
+const howMuchIsCustomPresent: MatchScoreCalculator = createMatchScoreCalculator(options);
+```
+
+## Hiragana
+
+```ts
+const options: CreateValidatorOptions = {
+ characterSets: [CharacterSet.Hiragana],
+};
+```
+
+## Hiragana + CJK Symbols & Punctuations
+
+```ts
+const options: CreateValidatorOptions = {
+ characterSets: [CharacterSet.Hiragana, CharacterSet.CJKPunctuations],
+};
+```
+
+## Hiragana + All Katakana
+
+```ts
+const options: CreateValidatorOptions = {
+ characterSets: [
+ CharacterSet.Hiragana,
+ CharacterSet.Katakana,
+ CharacterSet.KatakanaPhoneticExtension,
+ CharacterSet.HalfWidthKatakana,
+ ],
+};
+```
+
+## Hiragana + All Kanji
+
+```ts
+const options: CreateValidatorOptions = {
+ characterSets: [
+ CharacterSet.Hiragana,
+ CharacterSet.RareKanji,
+ CharacterSet.CommonUncommonKanji,
+ CharacterSet.KanjiCompatibilityIdeographs,
+ ],
+};
+```
+
+## All fullwidth letters
+
+```ts
+const options: CreateValidatorOptions = {
+ characterSets: [
+ CharacterSet.FullWidthUpperCase,
+ CharacterSet.FullWidthLowerCase,
+ CharacterSet.FullWidthNumbers,
+ CharacterSet.FullWidthPunctuations,
+ ],
+};
+```
+
+## Latin punctuations + All Japanese
+
+```ts
+// includes: " " (space), !"#$%&'()*+,-./
+const LatinPunctuations: UnicodeRange = {
+ start: '\\u0020',
+ end: '\\u002f',
+};
+
+// includes: :;<=>?@
+const LatinPunctuationsExtensionA: UnicodeRange = {
+ start: '\\u003a',
+ end: '\\u0040',
+};
+
+// includes: [\]^_`
+const LatinPunctuationsExtensionB: UnicodeRange = {
+ start: '\\u005b',
+ end: '\\u0060',
+};
+
+// includes: {|}~
+const LatinPunctuationsExtensionC: UnicodeRange = {
+ start: '\\u007b',
+ end: '\\u007e',
+};
+
+const LatinPunctuationsRanges: UnicodeRange[] = [
+ LatinPunctuations,
+ LatinPunctuationsExtensionA,
+ LatinPunctuationsExtensionB,
+ LatinPunctuationsExtensionC,
+];
+
+const options: CreateValidatorOptions = {
+ characterSets: [
+ CharacterSet.CJKPunctuations,
+ CharacterSet.Hiragana,
+ CharacterSet.Katakana,
+ CharacterSet.KatakanaPhoneticExtension,
+ CharacterSet.RareKanji,
+ CharacterSet.CommonUncommonKanji,
+ CharacterSet.KanjiCompatibilityIdeographs,
+ CharacterSet.HalfWidthKatakana,
+ CharacterSet.FullWidthUpperCase,
+ CharacterSet.FullWidthLowerCase,
+ CharacterSet.FullWidthNumbers,
+ CharacterSet.FullWidthPunctuations,
+ ],
+ customRanges: [...LatinPunctuationsRanges],
+};
+```
diff --git a/docs/release-flow.gif b/docs/release-flow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f34e09713cea2369dd3c6e9174c703440db2a20d
GIT binary patch
literal 646792
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