Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 45 additions & 57 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,57 +1,45 @@
name: CD

on:
push:
branches:
- master

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
# Check out as an admin to allow for pushing back to master
token: ${{ secrets.GH_OAUTH_TOKEN }}
# We need to fetch all tags and branches
fetch-depth: 0

- name: Use Node.js environment
uses: actions/setup-node@v2
with:
node-version: '14.x'
registry-url: https://registry.npmjs.org/

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install dependencies
run: yarn --frozen-lockfile

- name: Build all
run: yarn build

- name: Creates release if necessary
if: github.ref == 'refs/heads/master'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm whoami

OLD_PACKAGE_VERSION=$(echo $(git show HEAD~1:package.json) | jq '.version')
NEW_PACKAGE_VERSION=$(echo $(git show HEAD:package.json) | jq '.version')

if [ "$NEW_PACKAGE_VERSION" != "$OLD_PACKAGE_VERSION" ]; then
printf "Changed version [%s]\n" "$NEW_PACKAGE_VERSION"

npm publish --access public
fi
name: CD

on:
push:
branches:
- master

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Check out as an admin to allow for pushing back to master
token: ${{ secrets.GH_OAUTH_TOKEN }}
# We need to fetch all tags and branches
fetch-depth: 0

- name: Use Node.js environment
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: https://registry.npmjs.org/

- name: Install dependencies
run: yarn --frozen-lockfile

- name: Build all
run: yarn build

- name: Creates release if necessary
if: github.ref == 'refs/heads/master'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm whoami

OLD_PACKAGE_VERSION=$(echo $(git show HEAD~1:package.json) | jq '.version')
NEW_PACKAGE_VERSION=$(echo $(git show HEAD:package.json) | jq '.version')

if [ "$NEW_PACKAGE_VERSION" != "$OLD_PACKAGE_VERSION" ]; then
printf "Changed version [%s]\n" "$NEW_PACKAGE_VERSION"

npm publish --access public
fi
19 changes: 3 additions & 16 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,15 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
# we actually need "github.event.pull_request.commits + 1" commit
fetch-depth: 0

- name: Use Node.js environment
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: '14.x'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
node-version: '20.x'

- name: Install dependencies
run: yarn install
Expand All @@ -36,4 +24,3 @@ jobs:

- name: Run tests
run: yarn test --coverage

8 changes: 4 additions & 4 deletions .github/workflows/linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ jobs:
pull-requests: write

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: "16"
node-version: "20"

- run: yarn install

- name: Run eslint with reviewdog
uses: reviewdog/action-eslint@v1.16.1
uses: reviewdog/action-eslint@v1.33.0
with:
eslint_flags: . --ext .js,.jsx,.ts,.tsx
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
},
"dependencies": {
"@sentry/react": "^8.9.2",
"react": "^18.2.0"
"cpf-cnpj-validator": "2.1.0",
"react": "^18.2.0",
"vanilla-masker": "^1.2.0"
},
"devDependencies": {
"@types/jest": "^29.2.3",
Expand All @@ -27,6 +29,7 @@
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.2.2",
"jest-environment-jsdom": "29",
"prettier": "^2.3.2",
"ts-jest": "^29.0.3",
"ts-node-dev": "^2.0.0",
Expand Down
14 changes: 8 additions & 6 deletions src/formatters/formatCnpj.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import VMasker from 'vanilla-masker';
import { MASKS } from '../masks/masks';
import { stripAlphanumeric } from '../masks/strip';

const formatCnpj = (value: string): string => {
const formattedValue = value.replace(/\D/g, '').slice(0, 14);
if (!value) return '';

const stripped = stripAlphanumeric(value).slice(0, 14);

return formattedValue
.replace(/^(\d{2})(\d)/, '$1.$2')
.replace(/^(\d{2})\.(\d{3})(\d)/, '$1.$2.$3')
.replace(/^(\d{2})\.(\d{3})\.(\d{3})(\d)/, '$1.$2.$3/$4')
.replace(/^(\d{2})\.(\d{3})\.(\d{3})\/(\d{4})(\d)/, '$1.$2.$3/$4-$5');
return VMasker.toPattern(stripped, MASKS.CNPJ);
};

export default formatCnpj;
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export { default as isCpf } from './validations/isCpf';
export { default as isCnpj } from './validations/isCnpj';
export { default as isPis } from './validations/isPis';

// masks
export { default as maskValue } from './masks/maskValue';
export { default as maskInput } from './masks/maskInput';
export { default as maskCpf } from './masks/maskCpf';
export { default as maskCpfOrCnpj } from './masks/maskCpfOrCnpj';
export { default as maskComplete } from './masks/maskComplete';
export { default as setupMultipleMask } from './masks/setupMultipleMask';
export { default as maskHintBankAccount } from './masks/maskHintBankAccount';
export type { MaskType, CurrencyMaskOptions, BankCompensationCode } from './masks/types';

// breakpoints

export { default as breakpointFrom } from './styles/breakpointFrom';
Expand Down
48 changes: 48 additions & 0 deletions src/masks/maskComplete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { MASKS } from './masks';
import { stripAlphanumeric, stripNumeric, PATTERN_PLACEHOLDER_REGEX } from './strip';
import type { MaskType } from './types';

const ALPHANUMERIC_MASKS: ReadonlyArray<MaskType> = ['cnpj', 'cpf_cnpj'];

const placeholdersInPattern = (pattern: string): number => {
const matches = pattern.match(PATTERN_PLACEHOLDER_REGEX);
return matches ? matches.length : 0;
};

const patternFor = (type: MaskType): string | null => {
switch (type) {
case 'cpf': return MASKS.CPF;
case 'cnpj': return MASKS.CNPJ;
case 'phone': return MASKS.PHONE;
case 'zipcode': return MASKS.ZIPCODE;
case 'date': return MASKS.DATE;
case 'barCode': return MASKS.BAR_CODE;
case 'barCodeUtilities': return MASKS.BAR_CODE_UTILITIES;
case 'darf': return MASKS.DARF;
case 'number': return MASKS.NUMBER;
default: return null;
}
};

export default function maskComplete(value: string, type: MaskType): boolean {
if (!value) return false;

if (type === 'currency' || type === 'percentage') {
return value.length > 0;
}

if (type === 'cpf_cnpj') {
const stripped = stripAlphanumeric(value);
return stripped.length === 11 || stripped.length === 14;
}

const pattern = patternFor(type);
if (pattern === null) return false;

const expected = placeholdersInPattern(pattern);
const stripped = ALPHANUMERIC_MASKS.includes(type)
? stripAlphanumeric(value)
: stripNumeric(value);

return stripped.length === expected;
}
5 changes: 5 additions & 0 deletions src/masks/maskCpf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import maskValue from './maskValue';

export default function maskCpf(cpf: string): string {
return maskValue(cpf, 'cpf');
}
5 changes: 5 additions & 0 deletions src/masks/maskCpfOrCnpj.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import maskValue from './maskValue';

export default function maskCpfOrCnpj(value: string): string {
return maskValue(value, 'cpf_cnpj');
}
14 changes: 14 additions & 0 deletions src/masks/maskHintBankAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { BANK_ACCOUNT_MASKS } from './masks';
import type { BankCompensationCode } from './types';

const DEFAULT_HINT = '0000000000-0';

const toHint = (pattern: string): string => pattern.replace(/[9S]/g, '0');

export default function maskHintBankAccount(compensationCode: string): string {
const pattern = BANK_ACCOUNT_MASKS[compensationCode as BankCompensationCode];

if (!pattern) return DEFAULT_HINT;

return toHint(pattern);
}
21 changes: 21 additions & 0 deletions src/masks/maskInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import maskValue from './maskValue';
import type { MaskType, CurrencyMaskOptions } from './types';

export default function maskInput(
input: HTMLInputElement,
type: MaskType,
options?: CurrencyMaskOptions,
): () => void {
if (!input) return () => {};

const handler = (): void => {
input.value = maskValue(input.value, type, options);
};

input.value = maskValue(input.value, type, options);
input.addEventListener('input', handler);

return () => {
input.removeEventListener('input', handler);
};
}
55 changes: 55 additions & 0 deletions src/masks/maskValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import VMasker from 'vanilla-masker';
import { MASKS, CURRENCY_MASK_DEFAULTS, PERCENTAGE_MASK_DEFAULTS } from './masks';
import { stripAlphanumeric, stripNumeric } from './strip';
import type { MaskType, CurrencyMaskOptions } from './types';

const ALPHANUMERIC_MASKS: ReadonlyArray<MaskType> = ['cnpj', 'cpf_cnpj'];

const stripFor = (value: string, type: MaskType): string => (
ALPHANUMERIC_MASKS.includes(type) ? stripAlphanumeric(value) : stripNumeric(value)
);

const patternFor = (type: MaskType, stripped: string): string => {
if (type === 'cpf_cnpj') {
if (/[A-Z]/.test(stripped)) return MASKS.CNPJ;
return stripped.length <= 11 ? MASKS.CPF : MASKS.CNPJ;
}

switch (type) {
case 'cpf': return MASKS.CPF;
case 'cnpj': return MASKS.CNPJ;
case 'phone': return MASKS.PHONE;
case 'zipcode': return MASKS.ZIPCODE;
case 'date': return MASKS.DATE;
case 'barCode': return MASKS.BAR_CODE;
case 'barCodeUtilities': return MASKS.BAR_CODE_UTILITIES;
case 'darf': return MASKS.DARF;
case 'number': return MASKS.NUMBER;
default: return '';
}
};

export default function maskValue(
value: string | null | undefined,
type: MaskType,
options?: CurrencyMaskOptions,
): string {
if (value === null || value === undefined || value === '') return '';

const stringValue = String(value);

if (type === 'currency') {
return VMasker.toMoney(stringValue, { ...CURRENCY_MASK_DEFAULTS, ...(options || {}) });
}

if (type === 'percentage') {
return VMasker.toMoney(stringValue, { ...PERCENTAGE_MASK_DEFAULTS, ...(options || {}) });
}

const stripped = stripFor(stringValue, type);
const pattern = patternFor(type, stripped);

if (!pattern) return stringValue;

return VMasker.toPattern(stripped, pattern);
}
Loading
Loading