Skip to content

Commit

Permalink
feat: add translations for custom matchers
Browse files Browse the repository at this point in the history
* feat: get feedback from translations

* feat: add translations

* chore: update readme

* refactor: change patterns names

* chore: update README

* chore: update README

* tests: update test matcher strings

* fix: update translations + suggestions

* tests: add translations assertions

* fix: minor bug fix

* fix: minor bug fix

* chore: update readme

* chore: update readme

* fix: update matcher suggestions

* fix: add v4 beta as peer dependenvy

* chore: update readme regarding version

* fix: add translated suggestions to matchers

* tests: refactor tests to go through the zxcvbn-ts/core package

* tests: add suggestions assertions

* tests: remove redundant lines

---------

Co-authored-by: Ilan Kushnir <[email protected]>
  • Loading branch information
ilan-kushnir-payu-gpo and Ilan Kushnir authored Nov 27, 2024
1 parent 7e19b06 commit 69e1b95
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 135 deletions.
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# @zxcvbn-custom-matchers

The package is adding custom matchers to the zxcvbn-ts package. The matchers enforce specific character requirements in passwords and provide feedback and scoring.
> Please note! this package works only with zxcvbn-ts v.4 and above.
## Installation
```sh
Expand All @@ -9,25 +10,33 @@ npm install zxcvbn-custom-matchers

### Usage
```ts
import { zxcvbnOptions } from '@zxcvbn-ts/core';
import { ZxcvbnFactory } from '@zxcvbn-ts/core';
import {
lowercaseMatcher,
numberMatcher,
specialMatcher,
lowercaseMatcher,
uppercaseMatcher,
minLengthMatcher
minLengthMatcher,
customMatchersTranslations
} from 'zxcvbn-custom-matchers';

// Add the matchers
zxcvbnOptions.addMatcher('lowercase', lowercaseMatcher);
zxcvbnOptions.addMatcher('number', numberMatcher);
zxcvbnOptions.addMatcher('special', specialMatcher);
zxcvbnOptions.addMatcher('uppercase', uppercaseMatcher);
zxcvbnOptions.addMatcher('minLength', minLengthMatcher(10));
import { merge } from 'lodash';

// Add the custom matchers and their translations
const options = {
translations: merge({}, zxcvbnEnPackage.translations, customMatchersTranslations)
};
const customMatchers = {
minLength: minLengthMatcher(commons.MIN_PASSWORD_LENGTH),
specialRequired: specialMatcher,
numberRequired: numberMatcher,
lowercaseRequired: lowercaseMatcher,
uppercaseRequired: uppercaseMatcher
};
const zxcvbn = new ZxcvbnFactory(options, customMatchers);

// Use zxcvbn as usual
import { zxcvbn } from '@zxcvbn-ts/core';
const result = zxcvbn('password123');
const result = zxcvbn.check('password123');
console.log(result);
```

Expand Down
22 changes: 17 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@
"@types/mocha": "^10.0.10",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/core": "^4.0.0-beta.2",
"@zxcvbn-ts/language-en": "^3.0.2",
"chai": "^4.5.0",
"depcheck": "^1.4.3",
"eslint": "^8.18.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"husky": "^8.0.3",
"lint-staged": "^15.0.1",
"lodash": "^4.17.21",
"mocha": "^10.2.0",
"nyc": "^17.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"peerDependencies": {
"@zxcvbn-ts/core": "^4.0.0-beta.2"
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { uppercaseMatcher } from './matchers/uppercaseMatcher';
export { minLengthMatcher } from './matchers/minLengthMatcher';
export { uppercaseMatcher } from './matchers/uppercaseMatcher';
export { lowercaseMatcher } from './matchers/lowercaseMatcher';
export { numberMatcher } from './matchers/numberMatcher';
export { specialMatcher } from './matchers/specialMatcher';
export { customMatchersTranslations } from './translations';
13 changes: 8 additions & 5 deletions src/matchers/lowercaseMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types';
import { Matcher, Match } from '@zxcvbn-ts/core';

export const lowercaseMatcher: Matcher = {
Matching: class LowercaseMatcher {
Expand All @@ -9,14 +9,17 @@ export const lowercaseMatcher: Matcher = {
return matches;
}
}
matches.push({ pattern: 'lowercase', token: password, i: 0, j: password.length - 1 });
matches.push({ pattern: 'lowercaseRequired', token: password, i: 0, j: password.length - 1 });
return matches;
}
},
feedback(_match) {
return { warning: 'Include at least one lowercase letter.', suggestions: [] };
feedback: options => {
return {
warning: options.translations.warnings['lowercaseRequired'] || 'lowercaseRequired',
suggestions: [options.translations.suggestions['lowercaseRequired'] || 'lowercaseRequired'],
};
},
scoring(_match) {
scoring() {
return -100;
},
};
11 changes: 7 additions & 4 deletions src/matchers/minLengthMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types';
import { Matcher, Match } from '@zxcvbn-ts/core';

export const minLengthMatcher = (minLength: number): Matcher => ({
Matching: class MinLengthMatcher {
Expand All @@ -10,10 +10,13 @@ export const minLengthMatcher = (minLength: number): Matcher => ({
return matches;
}
},
feedback(_match) {
return { warning: `Password may not be shorter than ${minLength} characters.`, suggestions: [] };
feedback: options => {
return {
warning: options.translations.warnings['minLength']?.replace('%s', minLength) || 'minLength',
suggestions: [options.translations.suggestions['minLength']?.replace('%s', minLength) || 'minLength'],
};
},
scoring(_match) {
scoring() {
return -100;
},
});
13 changes: 8 additions & 5 deletions src/matchers/numberMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types';
import { Matcher, Match } from '@zxcvbn-ts/core';

export const numberMatcher: Matcher = {
Matching: class NumberMatcher {
Expand All @@ -9,14 +9,17 @@ export const numberMatcher: Matcher = {
return matches;
}
}
matches.push({ pattern: 'number', token: password, i: 0, j: password.length - 1 });
matches.push({ pattern: 'numberRequired', token: password, i: 0, j: password.length - 1 });
return matches;
}
},
feedback(_match) {
return { warning: 'Include at least one number.', suggestions: [] };
feedback: options => {
return {
warning: options.translations.warnings['numberRequired'] || 'numberRequired',
suggestions: [options.translations.suggestions['numberRequired'] || 'numberRequired'],
};
},
scoring(_match) {
scoring() {
return -100;
},
};
13 changes: 8 additions & 5 deletions src/matchers/specialMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types';
import { Matcher, Match } from '@zxcvbn-ts/core';

export const specialMatcher: Matcher = {
Matching: class SpecialMatcher {
Expand All @@ -10,14 +10,17 @@ export const specialMatcher: Matcher = {
return matches;
}
}
matches.push({ pattern: 'special', token: password, i: 0, j: password.length - 1 });
matches.push({ pattern: 'specialRequired', token: password, i: 0, j: password.length - 1 });
return matches;
}
},
feedback(_match) {
return { warning: 'Include at least one special character.', suggestions: [] };
feedback: options => {
return {
warning: options.translations.warnings['specialRequired'] || 'specialRequired',
suggestions: [options.translations.suggestions['specialRequired'] || 'specialRequired'],
};
},
scoring(_match) {
scoring() {
return -100;
},
};
13 changes: 8 additions & 5 deletions src/matchers/uppercaseMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types';
import { Matcher, Match } from '@zxcvbn-ts/core';

export const uppercaseMatcher: Matcher = {
Matching: class UppercaseMatcher {
Expand All @@ -9,14 +9,17 @@ export const uppercaseMatcher: Matcher = {
return matches;
}
}
matches.push({ pattern: 'uppercase', token: password, i: 0, j: password.length - 1 });
matches.push({ pattern: 'uppercaseRequired', token: password, i: 0, j: password.length - 1 });
return matches;
}
},
feedback(_match) {
return { warning: 'Include at least one uppercase letter.', suggestions: [] };
feedback: options => {
return {
warning: options.translations.warnings['uppercaseRequired'] || 'uppercaseRequired',
suggestions: [options.translations.suggestions['uppercaseRequired'] || 'uppercaseRequired'],
};
},
scoring(_match) {
scoring() {
return -100;
},
};
17 changes: 17 additions & 0 deletions src/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const customMatchersTranslations = {
warnings: {
lowercaseRequired: 'At least one lowercase letter is required.',
minLength: 'Password must be at least %s characters long.',
numberRequired: 'At least one number is required.',
specialRequired: 'At least one special character is required.',
uppercaseRequired: 'At least one uppercase letter is required.',
},
suggestions: {
lowercaseRequired: 'Include at least one lowercase letter.',
minLength: 'Password may not be shorter than %s characters.',
numberRequired: 'Include at least one number.',
specialRequired: 'Include at least one special character.',
uppercaseRequired: 'Include at least one uppercase letter.',
},
timeEstimation: {},
};
Loading

0 comments on commit 69e1b95

Please sign in to comment.