question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Translation problems with complex RegEx

See original GitHub issue
Overview of the issue

We generated an entity from a JDL file, and complex RegEx on the fields. The generated code in the HTML file executes with a JS error, due to escaping problems with the ngx-translate lib.

Motivation for or Use Case

A complex regex implies a runtime JS error in the generated code.

Reproduce the error

Here is an example of the JDL :

entity Candidate {
    companyName String required maxlength(32) pattern("^[a-zA-Z0-9 \-&,'\.]*$"),
    lastname String required maxlength(32) pattern("^[^\-][a-zA-Z0-9 \-']*[^\-']$")
}

The generated HTML is :

<small class="form-text text-danger"
                    [hidden]="!editForm.controls.companyName?.errors?.pattern" jhiTranslate="entity.validation.pattern" translateValues="{ pattern: '^[a-zA-Z0-9 \\-&amp;,&#39;\\.]*$' }">
                    This field should follow pattern "^[a-zA-Z0-9 \-&amp;,'\.]*$".
</small>
<small class="form-text text-danger"
                    [hidden]="!editForm.controls.lastname?.errors?.pattern" jhiTranslate="entity.validation.pattern" translateValues="{ pattern: '^[^\\-][a-zA-Z0-9 \\-&#39;]*[^\\-&#39;]$' }">
                    This field should follow pattern "^[^\-][a-zA-Z0-9 \-']*[^\-']$".
</small>

The error in the JS console is :

EXCEPTION: Error in ./JhiTranslateComponent class JhiTranslateComponent - inline template:0:6 caused by: Wrong parameter in TranslatePipe. Expected a valid Object, received: { ‘pattern’: ‘^[a-zA-Z0-9 \-&,’\.]*$’ }

Related issues
Suggest a Fix

I think there is a problem with the ngx-translate lib which tries to rewrite the regex :

                let validArgs: string = args[0]
                    .replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":')
                    .replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"');
                try {
                    interpolateParams = JSON.parse(validArgs);
                } catch(e) {
                    throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`);
                }

So perhaps JHipster should escape or transform some caracters, but we didn’t find the good combinaison for our regex.

JHipster Version(s)
recrutement@0.0.0 E:\projets\Prim\eura
+-- UNMET PEER DEPENDENCY @angular/compiler@2.4.7
+-- UNMET PEER DEPENDENCY @angular/core@2.4.7
`-- generator-jhipster@4.0.5
JHipster configuration
{
  "generator-jhipster": {
    "jhipsterVersion": "4.0.5",
    "baseName": "recrutement",
    "packageName": "com.prim.eura",
    "packageFolder": "com/prim/eura",
    "serverPort": "8080",
    "authenticationType": "session",
    "hibernateCache": "no",
    "clusteredHttpSession": false,
    "websocket": false,
    "databaseType": "sql",
    "devDatabaseType": "mssql",
    "prodDatabaseType": "mssql",
    "searchEngine": false,
    "messageBroker": false,
    "serviceDiscoveryType": false,
    "buildTool": "maven",
    "enableSocialSignIn": false,
    "rememberMeKey": "",
    "clientFramework": "angular2",
    "useSass": true,
    "clientPackageManager": "yarn",
    "applicationType": "monolith",
    "testFrameworks": [
      "cucumber"
    ],
    "jhiPrefix": "jhi",
    "otherModules": [
      {
        "name": "generator-jhipster-bootstrap-material-design",
        "version": "3.5.1"
      },
      {
        "name": "generator-jhipster-mssql",
        "version": "2.1.2"
      },
      {
        "name": "generator-jhipster-entity-audit-and-delete",
        "version": "2.2.2"
      },
      {
        "name": "generator-jhipster-webservice",
        "version": "0.1.0"
      }
    ],
    "enableTranslation": true,
    "nativeLanguage": "fr",
    "languages": [
      "fr",
      "en"
    ]
  }
}
Entity configuration(s) entityName.json files generated in the .jhipster directory

Candidate.json

{
    "fluentMethods": true,
    "relationships": [
        {
            "relationshipType": "one-to-one",
            "javadoc": "The user account for the candidate",
            "relationshipName": "user",
            "otherEntityName": "user",
            "otherEntityField": "id",
            "ownerSide": true,
            "otherEntityRelationshipName": "candidate"
        }
    ],
    "fields": [
        {
            "fieldName": "comment",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 44
        },
        {
            "fieldName": "msaNumber",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 3
        },
        {
            "fieldName": "siretNumber",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 14,
            "fieldValidateRulesPattern": "^([0-9]{14}|(et|ET|Et|eT)[0-9]{11})$"
        },
        {
            "fieldName": "companyName",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "companyAddress1",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "companyAddress2",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "companyPC",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 5,
            "fieldValidateRulesPattern": "^[0-9]*$"
        },
        {
            "fieldName": "companyCity",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 27,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "lastname",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[^\\-][a-zA-Z0-9 \\-']*[^\\-']$"
        },
        {
            "fieldName": "firstname",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[^\\-][a-zA-Z0-9 \\-']*[^\\-']$"
        },
        {
            "fieldName": "matricule",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 13,
            "fieldValidateRulesPattern": "^[0-9]{13}$"
        },
        {
            "fieldName": "birthDate",
            "fieldType": "ZonedDateTime",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "birthInsee",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 3
        },
        {
            "fieldName": "hireDate",
            "fieldType": "ZonedDateTime",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "codeNaf",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 5,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9]*$"
        },
        {
            "fieldName": "usename",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 32,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-']*$"
        },
        {
            "fieldName": "sex",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 1,
            "fieldValidateRulesPattern": "^(M|F)$"
        },
        {
            "fieldName": "birthDepartment",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 2
        },
        {
            "fieldName": "contractType",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 2
        },
        {
            "fieldName": "cddEndDate",
            "fieldType": "ZonedDateTime"
        },
        {
            "fieldName": "trialPeriod",
            "fieldType": "Integer",
            "fieldValidateRules": [
                "required",
                "max"
            ],
            "fieldValidateRulesMax": 999
        },
        {
            "fieldName": "phone",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 20
        },
        {
            "fieldName": "suffixDelegationAccount",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 5
        },
        {
            "fieldName": "btapeCode",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 4
        },
        {
            "fieldName": "dpaeStatus",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 1
        },
        {
            "fieldName": "employerExemptionOccasionalWorker",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "employerExemptionJobseeker",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "noisyWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "vribatingWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "bioHazardWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "nigthWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "chemicalHazardWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "otherRisksWork",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 40
        },
        {
            "fieldName": "aptEquivalentWork",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "disabledWorker",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "longTimeSeasonal",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "aptEquivalentWorkSameEmployer",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "aptEquivalentWorkOtherEmployer",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "addressComplement",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 25,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "addressNumber",
            "fieldType": "Integer",
            "fieldValidateRules": [
                "max"
            ],
            "fieldValidateRulesMax": 9999
        },
        {
            "fieldName": "addressBtq",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 1
        },
        {
            "fieldName": "addressLaneType",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 4
        },
        {
            "fieldName": "addressLane",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 25,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "addressPC",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 5,
            "fieldValidateRulesPattern": "^[0-9]*$"
        },
        {
            "fieldName": "addressCity",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 25,
            "fieldValidateRulesPattern": "^[a-zA-Z0-9 \\-&,'\\.]*$"
        },
        {
            "fieldName": "addressCountry",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "maxlength",
                "pattern"
            ],
            "fieldValidateRulesMaxlength": 3,
            "fieldValidateRulesPattern": "^[0-9]*$"
        },
        {
            "fieldName": "foreignResidentTax",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "workLabel",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 25
        },
        {
            "fieldName": "candidateType",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 3
        },
        {
            "fieldName": "technicalWorker",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "salaryGross",
            "fieldType": "Float",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "hierarchyLevel",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 5
        },
        {
            "fieldName": "partialTimeRate",
            "fieldType": "Float"
        },
        {
            "fieldName": "workplaceDepartment",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 2
        },
        {
            "fieldName": "workplaceInsee",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 3
        },
        {
            "fieldName": "executive",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "agirc",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "articleFour",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "articleThirtySix",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "paidKind",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "taskPaid",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "equivalentSchedule",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "excludedMonthlyPay",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "seasonal",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "renewableCdd",
            "fieldType": "Boolean",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "cddLength",
            "fieldType": "Integer",
            "fieldValidateRules": [
                "max"
            ],
            "fieldValidateRulesMax": 999
        },
        {
            "fieldName": "previousCddEndDate",
            "fieldType": "ZonedDateTime"
        },
        {
            "fieldName": "cddEndCause",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 2
        },
        {
            "fieldName": "fileReference",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 36
        },
        {
            "fieldName": "transmitCanal",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 1
        },
        {
            "fieldName": "email",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 50
        },
        {
            "fieldName": "mobile",
            "fieldType": "String",
            "fieldValidateRules": [
                "maxlength"
            ],
            "fieldValidateRulesMaxlength": 20
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "candidate",
    "dto": "no",
    "pagination": "infinite-scroll",
    "service": "serviceImpl"
}

Contract.json

{
    "fluentMethods": true,
    "relationships": [
        {
            "relationshipType": "many-to-one",
            "relationshipName": "candidate",
            "otherEntityName": "candidate",
            "otherEntityField": "id"
        },
        {
            "relationshipType": "many-to-one",
            "relationshipName": "template",
            "otherEntityName": "contractTemplate",
            "otherEntityField": "id"
        }
    ],
    "fields": [
        {
            "fieldName": "label",
            "fieldType": "String"
        },
        {
            "fieldName": "path",
            "fieldType": "String"
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "contract",
    "dto": "no",
    "pagination": "infinite-scroll",
    "service": "serviceImpl"
}

ContractTemplate.json

{
    "fluentMethods": true,
    "relationships": [],
    "fields": [
        {
            "fieldName": "label",
            "fieldType": "String"
        },
        {
            "fieldName": "code",
            "fieldType": "String"
        },
        {
            "fieldName": "filename",
            "fieldType": "String"
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "contract_template",
    "dto": "no",
    "pagination": "no",
    "service": "no"
}

ContractTemplateMapping.json

{
    "fluentMethods": true,
    "relationships": [
        {
            "relationshipType": "many-to-one",
            "relationshipName": "template",
            "otherEntityName": "contractTemplate",
            "otherEntityField": "id"
        }
    ],
    "fields": [
        {
            "fieldName": "label",
            "fieldType": "String"
        },
        {
            "fieldName": "code",
            "fieldType": "String"
        },
        {
            "fieldName": "mappingValue",
            "fieldType": "String"
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "contract_template_mapping",
    "dto": "no",
    "pagination": "no",
    "service": "no"
}

DpaeGeneration.json

{
    "fluentMethods": true,
    "relationships": [],
    "fields": [
        {
            "fieldName": "type",
            "fieldType": "DpaeGenerationType",
            "fieldValues": "OK,KO"
        },
        {
            "fieldName": "filename",
            "fieldType": "String"
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "dpae_generation",
    "dto": "no",
    "pagination": "infinite-scroll",
    "service": "serviceImpl"
}

Validation.json

{
    "fluentMethods": true,
    "relationships": [
        {
            "relationshipType": "many-to-one",
            "javadoc": "The candidate to validate",
            "relationshipName": "candidate",
            "otherEntityName": "candidate",
            "otherEntityField": "id"
        },
        {
            "relationshipType": "many-to-one",
            "javadoc": "The user validating the candidate",
            "relationshipName": "user",
            "otherEntityName": "user",
            "otherEntityField": "id"
        }
    ],
    "fields": [
        {
            "fieldName": "status",
            "fieldType": "ValidationStatus",
            "fieldValues": "PENDING,VALIDATED,REFUSED"
        },
        {
            "fieldName": "rank",
            "fieldType": "Integer"
        }
    ],
    "changelogDate": "20170220135511",
    "entityTableName": "validation",
    "dto": "no",
    "pagination": "no",
    "service": "no"
}
Browsers and Operating System

java version “1.8.0_112” Java™ SE Runtime Environment (build 1.8.0_112-b15) Java HotSpot™ 64-Bit Server VM (build 25.112-b15, mixed mode)

git version 2.10.1.windows.1

node: v6.9.5

npm: 3.10.10

yeoman: 1.8.5

yarn: 0.20.3

Docker version 1.13.1, build 092cba3

docker-compose version 1.11.1, build 7afaa436

  • Checking this box is mandatory (this is just to show you read everything)

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:7 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
gmarzioucommented, Feb 21, 2017

What’s the use of displaying a regexp to a user who may not understand it? The error message should explain the expected format in human words probably with an example.

So I would rather be in favor of not generating error messages including regex patterns.

0reactions
deepu105commented, Mar 11, 2017

The easiest fix would be to pass the filed name instead of the pattern for the translation as below as the developer need to write appropriate message as per business need anyway. So no point in spending lot of effort here. Plz go ahead if some one wants to PR this as its not high priority for me

<small class="form-text text-danger"
                    [hidden]="!editForm.controls.companyName?.errors?.pattern" jhiTranslate="entity.validation.pattern" translateValues="{ pattern: 'companyName' }">
                    This field should follow pattern for "companyName".
</small>
<small class="form-text text-danger"
                    [hidden]="!editForm.controls.lastname?.errors?.pattern" jhiTranslate="entity.validation.pattern" translateValues="{ pattern: 'lastname' }">
                    This field should follow pattern for "lastname".
</small>
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to create complex Regular Expressions (regex) conditions
This document will provide some guidance and use cases to show how you can use regex to create complex conditions and superpower your...
Read more >
How To Use Regular Expressions To Identify Translatable ...
Some translation and localization projects involve different layers of information and content, resulting in more complex lines of code. Let's ...
Read more >
The irregularities of regular expressions in #memoQ
A lot of memoQ users think that regex is irrelevant to their working lives, but for hardcore financial and legal translators at least, ......
Read more >
Regular Expressions: Now You Have Two Problems
Calm down. Take a deep breath. Relax. Let me be very clear on this point: If you read an incredibly complex, impossible to...
Read more >
Translating regular expression matching into transducers
Since regular expression matching in most other scripting languages is based on that in Perl, the translation is applicable to other languages including...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found