{"id":25858559,"url":"https://github.com/hlysine/readable-regexp","last_synced_at":"2026-05-10T01:57:01.230Z","repository":{"id":46603710,"uuid":"515488770","full_name":"hlysine/readable-regexp","owner":"hlysine","description":"Regular Expressions - quick and concise, readable and composable.","archived":false,"fork":false,"pushed_at":"2023-09-10T02:44:14.000Z","size":750,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-19T23:23:28.552Z","etag":null,"topics":["readable","regex","regexp","regular-expression","regular-expressions","typescript"],"latest_commit_sha":null,"homepage":"https://hlysine.gitbook.io/readable-regexp/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hlysine.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-19T07:54:47.000Z","updated_at":"2023-10-09T12:29:50.000Z","dependencies_parsed_at":"2022-08-31T17:12:28.350Z","dependency_job_id":null,"html_url":"https://github.com/hlysine/readable-regexp","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlysine%2Freadable-regexp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlysine%2Freadable-regexp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlysine%2Freadable-regexp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlysine%2Freadable-regexp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hlysine","download_url":"https://codeload.github.com/hlysine/readable-regexp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240072076,"owners_count":19743527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["readable","regex","regexp","regular-expression","regular-expressions","typescript"],"created_at":"2025-03-01T20:21:24.992Z","updated_at":"2026-05-10T01:57:01.173Z","avatar_url":"https://github.com/hlysine.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003ereadable-regexp\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![CI](https://github.com/hlysine/readable-regexp/actions/workflows/main.yml/badge.svg)](https://github.com/hlysine/readable-regexp/actions/workflows/main.yml)\n[![Coverage Status](https://coveralls.io/repos/github/hlysine/readable-regexp/badge.svg?branch=main)](https://coveralls.io/github/hlysine/readable-regexp?branch=main)\n[![TypeScript](https://img.shields.io/badge/built%20with-TypeScript-blue)](https://www.typescriptlang.org/)\n[![npm](https://img.shields.io/npm/v/readable-regexp)](https://www.npmjs.com/package/readable-regexp)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/readable-regexp)](https://www.npmjs.com/package/readable-regexp)\n[![Dependency Count](https://img.shields.io/badge/dynamic/json?url=https://api.npmutil.com/package/readable-regexp\u0026label=dependencies\u0026query=$.dependencies.count)](https://www.npmjs.com/package/readable-regexp)\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\nRegular Expressions - quick and concise, readable and composable.\n\u003c/p\u003e\n\n[![Quick example of readable-regexp](https://github.com/hlysine/readable-regexp/assets/25472513/3c0540d5-1b25-4f83-afb4-7ed16a3a5140)](https://hlysine.gitbook.io/readable-regexp/)\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e • \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e • \u003ca href=\"#quick-start--documentation\"\u003eQuick Start / Documentation\u003c/a\u003e\n\u003c/p\u003e\n\n## Features\n\n### 📖 Readable\n\nBe explicit and extract common pieces\n\n\u003cdetails\u003e\n    \u003csummary\u003eClick to see examples\u003c/summary\u003e\n\n--------------------------------\n\nCompare a readable-regexp expression:\n\n```js\nconst num = capture.oneOf(\n  oneOrMore.digit, // integer\n  zeroOrMore.digit.exactly`.`.oneOrMore.digit // decimal\n);\nconst regExp = match(num).exactly`,`.maybe` `.match(num).toRegExp(Flag.Global); // num is used twice here\n```\n\nWith normal JS RegExp:\n\n```js\nconst regExp = /(\\d+|\\d*\\.\\d+), ?(\\d+|\\d*\\.\\d+)/g; // we have to copy-paste the capture group\n```\n\nIn a more complex use case, we can destructure the expression into manageable small parts:\n\n```js\nconst allowedChar = notCharIn`\u003c\u003e()[]\\\\\\\\` `.,;:@\"` (whitespace);\n\nconst username =\n  oneOrMore.match(allowedChar)\n  .zeroOrMore(\n    exactly`.`\n    .oneOrMore.match(allowedChar)\n  );\n\nconst quotedString =\n  exactly`\"`\n  .oneOrMore.char\n  .exactly`\"`;\n\nconst ipv4Address =\n  exactly`[`\n  .repeat(1, 3).digit\n  .exactly`.`\n  .repeat(1, 3).digit\n  .exactly`.`\n  .repeat(1, 3).digit\n  .exactly`.`\n  .repeat(1, 3).digit\n  .exactly`]`;\n\nconst domainName =\n  oneOrMore(\n    oneOrMore.charIn`a-z` `A-Z` `0-9` `-`\n    .exactly`.`\n  )\n  .atLeast(2).charIn`a-z` `A-Z`;\n\nconst email =\n  lineStart\n  .capture.oneOf(username, quotedString)\n  .exactly`@`\n  .capture.oneOf(ipv4Address, domainName)\n  .lineEnd\n  .toRegExp();\n```\n\nThis is far more readable and debuggable than the equivalent RegExp:\n\n```js\nconst email =\n  /^([^\u003c\u003e()[\\]\\\\.,;:@\"\\s]+(?:\\.[^\u003c\u003e()[\\]\\\\.,;:@\"\\s]+)*|\".+\")@(\\[\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\]|(?:[a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,})$/;\n```\n\n--------------------------------\n\n\u003c/details\u003e\n\n### 📐 Flexible and Concise\n\nMultiple shorthands and syntax options\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick to see examples\u003c/summary\u003e\n\n--------------------------------\n\nWithout all the shorthands, an expression looks like this:\n\n```js\nconst regExp = exactly('[')\n  .captureAs('timestamp')(oneOrMore(not(charIn(']'))))\n  .exactly('] ')\n  .captureAs('category')(oneOrMore(word).exactly('-').oneOrMore(word))\n  .exactly(': ')\n  .captureAs('message')(oneOrMore(char))\n  .toRegExp('gm');\n```\n\nWhenever a function takes a single string literal, you can use a tagged template literal to remove the brackets:\n\n```js\nconst regExp = exactly`[`\n  .captureAs`timestamp`(oneOrMore(not(charIn`]`)))\n  .exactly`] `\n  .captureAs`category`(oneOrMore(word).exactly`-`.oneOrMore(word))\n  .exactly`: `\n  .captureAs`message`(oneOrMore(char))\n  .toRegExp`gm`;\n```\n\nWhen there is only one token in a quantifier or group, you can chain it with `.` instead of using a bracket:\n\n```js\nconst regExp = exactly`[`\n  .captureAs`timestamp`.oneOrMore.not.charIn`]`\n  .exactly`] `\n  .captureAs`category`(oneOrMore.word.exactly`-`.oneOrMore.word)\n  .exactly`: `\n  .captureAs`message`.oneOrMore.char\n  .toRegExp`gm`;\n```\n\nThere are shorthands for negating a character class or a lookaround:\n\n```js\nconst regExp = exactly`[`\n  .captureAs`timestamp`.oneOrMore.notCharIn`]`\n  .exactly`] `\n  .captureAs`category`(oneOrMore.word.exactly`-`.oneOrMore.word)\n  .exactly`: `\n  .captureAs`message`.oneOrMore.char\n  .toRegExp`gm`;\n```\n\nAs you can see, most of the distracting brackets are gone, and you are left with a clean and concise expression.\n\n--------------------------------\n\n\u003c/details\u003e\n\n### 🛟 Safe\n\nType check, auto-complete, and runtime safeguards\n\n\u003cdetails\u003e\n    \u003csummary\u003eClick to see examples\u003c/summary\u003e\n\n--------------------------------\n\nSome errors can be avoided just by writing in readable-regexp:\n\n```js\nconst o = 'Ȯ'; // 0x022e\nconst result1 = /\\u22e/.test(n);\n// false\n\nconst result2 = unicode`22e`.toRegExp().test(n);\n// true\n// '22e' is automatically fixed to be '\\u022e'\n```\n\nSome errors can be caught by TypeScript at compile time:\n\n**(Not working at the moment. These errors will either be thrown at runtime or be handled by readable-regexp to produce reasonable RegExp.)**\n\n```js\n// @ts-expect-error - You cannot use two quantifiers on one token\nconst regExp = oneOrMore.zeroOrMore`foo`;\n```\n\n```js\n// @ts-expect-error - char is not negatable, because it matches nothing\nconst regExp = oneOrMore.not.char;\n```\n\n```js\n// @ts-expect-error - k is not a valid flag\nconst regExp = char.toRegExp('gki');\n```\n\nSome can be caught at run time:\n\n```js\nconst result1 = /(foo)\\2/.test('foofoo');\n// false\n\nconst result2 = capture`foo`.ref(2).toRegExp().test('foofoo');\n// Error: The following backreferences are not defined: 2\n```\n\n--------------------------------\n\n\u003c/details\u003e\n\n## Installation\n\n### With a package manager\n\n```bash\nnpm install readable-regexp\n\nyarn add readable-regexp\n```\n\n```js\nimport { oneOrMore, exactly } from 'readable-regexp';\n\nconst { oneOrMore, exactly } = require('readable-regexp');\n```\n\n### With a CDN\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/readable-regexp/dist/readable-regexp.umd.js\"\u003e\u003c/script\u003e\n```\n\n```js\nconst { oneOrMore, exactly } = readableRegExp;\n```\n\n## Quick Start / Documentation\n\n| [**Quick Start**](https://hlysine.gitbook.io/readable-regexp/getting-started/installation) |\n|:------------------------------------------------------------------------------------------:|\n| [**Documentation**](https://hlysine.gitbook.io/readable-regexp/)                           |\n| [**TypeDoc**](https://hlysine.github.io/readable-regexp/)                                  |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlysine%2Freadable-regexp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhlysine%2Freadable-regexp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlysine%2Freadable-regexp/lists"}