@ptolemy2002/rgx 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # RGX
2
+ A library for easy construction and validation of regular expressions in TypeScript. You can use `rgx` to concatenate various types of tokens into a valid regular expression string, with type safety and validation.
3
+
4
+ ## Type Reference
5
+ ```typescript
6
+ import { Branded } from "@ptolemy2002/ts-brand-utils";
7
+
8
+ type RGXNoOpToken = null | undefined;
9
+ type RGXNativeToken = string | number | boolean | RGXNoOpToken;
10
+ type RGXConvertibleToken = { toRgx: () => RGXNativeToken | RGXNativeToken[] };
11
+ type RGXToken = RGXNativeToken | RGXConvertibleToken | RGXToken[];
12
+
13
+ const validRegexSymbol = Symbol('ValidRegex');
14
+ type ValidRegexBrandSymbol = typeof validRegexSymbol;
15
+ type ValidRegexString = Branded<string, [ValidRegexBrandSymbol]>;
16
+
17
+ type RGXTokenType = 'no-op' | 'native' | 'convertible' | RGXTokenType[];
18
+ type RGXTokenFromType<T extends RGXTokenType> =
19
+ // ... see source for full definition
20
+ ;
21
+ ```
22
+
23
+ ## Functions
24
+ The following functions are exported by the library:
25
+
26
+ ### isRGXNoOpToken
27
+ ```typescript
28
+ function isRGXNoOpToken(value: unknown): value is RGXNoOpToken
29
+ ```
30
+
31
+ Checks if the given value is a no-op token (`null` or `undefined`).
32
+
33
+ #### Parameters
34
+ - `value` (`unknown`): The value to check.
35
+
36
+ #### Returns
37
+ - `boolean`: `true` if the value is a no-op token, otherwise `false`.
38
+
39
+ ### isRGXNativeToken
40
+ ```typescript
41
+ function isRGXNativeToken(value: unknown): value is RGXNativeToken
42
+ ```
43
+
44
+ Checks if the given value is a native token (string, number, boolean, or no-op).
45
+
46
+ #### Parameters
47
+ - `value` (`unknown`): The value to check.
48
+
49
+ #### Returns
50
+ - `boolean`: `true` if the value is a native token, otherwise `false`.
51
+
52
+ ### isRGXConvertibleToken
53
+ ```typescript
54
+ function isRGXConvertibleToken(value: unknown): value is RGXConvertibleToken
55
+ ```
56
+
57
+ Checks if the given value is a convertible token (an object with a `toRgx` method). Validates that `toRgx` is callable and returns a valid RGX native token or array of native tokens.
58
+
59
+ #### Parameters
60
+ - `value` (`unknown`): The value to check.
61
+
62
+ #### Returns
63
+ - `boolean`: `true` if the value is a convertible token, otherwise `false`.
64
+
65
+ ### rgxTokenType
66
+ ```typescript
67
+ function rgxTokenType(value: RGXToken): RGXTokenType
68
+ ```
69
+
70
+ Determines the type of a given RGX token (`no-op`, `native`, `convertible`, or an array of the former).
71
+
72
+ If you narrow the result of this function to something more specific, you can then convert these string or array literals into their corresponding token types using the `RGXTokenFromType` utility type or `rgxTokenFromType` function.
73
+
74
+ ```typescript
75
+ const token: RGXToken = ...;
76
+ const type = rgxTokenType(token);
77
+
78
+ if (type === 'native') {
79
+ const narrowedToken1 = token as RGXTokenFromType<typeof type>; // narrowedToken is RGXNativeToken
80
+ const narrowedToken2 = rgxTokenFromType(type, token); // same as above
81
+ }
82
+ ```
83
+
84
+ #### Parameters
85
+ - `value` (`RGXToken`): The RGX token to check.
86
+
87
+ #### Returns
88
+ - `RGXTokenType`: The type of the RGX token.
89
+
90
+ ### rgxTokenFromType
91
+ ```typescript
92
+ function rgxTokenFromType<T extends RGXTokenType>(type: T, value: RGXToken): RGXTokenFromType<T>
93
+ ```
94
+
95
+ Does nothing at runtime, but performs a type assertion to the correct subset of `RGXToken` based on the provided `RGXTokenType`.
96
+
97
+ #### Parameters
98
+ - `type` (`T`): The RGX token type to assert to.
99
+ - `value` (`RGXToken`): The RGX token to assert.
100
+
101
+ #### Returns
102
+ - `RGXTokenFromType<T>`: The input value, but with its type asserted to the corresponding token type based on the provided `RGXTokenType`.
103
+
104
+ ### isValidRegex
105
+ ```typescript
106
+ function isValidRegex(value: string): value is ValidRegexString
107
+ ```
108
+
109
+ Checks if the given string is a valid regular expression by attempting to create a new `RegExp` object with it. If it succeeds, the string is branded as a `ValidRegexString`.
110
+
111
+ #### Parameters
112
+ - `value` (`string`): The string to check.
113
+
114
+ #### Returns
115
+ - `boolean`: `true` if the string is a valid regular expression, otherwise `false`.
116
+
117
+ ### escapeRegex
118
+ ```typescript
119
+ function escapeRegex(value: string): ValidRegexString
120
+ ```
121
+
122
+ Escapes special regex characters in the given string and brands the result as a `ValidRegexString`.
123
+
124
+ #### Parameters
125
+ - `value` (`string`): The string to escape.
126
+
127
+ #### Returns
128
+ - `ValidRegexString`: The escaped string, branded as a valid regex string.
129
+
130
+ ### resolveRGXToken
131
+ ```typescript
132
+ function resolveRGXToken(token: RGXToken): string
133
+ ```
134
+
135
+ Resolves an RGX token to a string. No-op tokens resolve to an empty string, native tokens are converted to strings and escaped, convertible tokens are converted using their `toRgx` method and then resolved recursively, and arrays of tokens are resolved as unions of their resolved elements (placed in a non-capturing group).
136
+
137
+ #### Parameters
138
+ - `token` (`RGXToken`): The RGX token to resolve.
139
+
140
+ #### Returns
141
+ - `string`: The resolved string representation of the RGX token.
142
+
143
+ ### rgx
144
+ ```typescript
145
+ function rgx(strings: TemplateStringsArray, ...tokens: RGXToken[]): RegExp
146
+ ```
147
+
148
+ A template tag function that constructs a `RegExp` object from the provided template literal. The template literal can contain RGX tokens, which will be resolved and concatenated with the literal parts to form the final regex pattern.
149
+
150
+ Example usages:
151
+ ```typescript
152
+ const beginning = /^/;
153
+ const end = /$/;
154
+ const word = /\w+/;
155
+ const pattern = rgx`${beginning}testing ${word}${end}`; // /^testing \w+$/ - matches the string "testing " followed by a word, anchored to the start and end of the string
156
+
157
+ const optionalDigit = /\d?/;
158
+ const pattern2 = rgx`${beginning}optional digit: ${optionalDigit}${end}`; // /^optional digit: \d?$/ - matches the string "optional digit: " followed by an optional digit, anchored to the start and end of the string
159
+
160
+ const pattern3 = rgx`${beginning}value: ${[word, optionalDigit]}${end}`; // /^value: (?:\w+|\d?)$/ - matches the string "value: " followed by either a word or an optional digit, anchored to the start and end of the string
161
+ ```
162
+
163
+ #### Parameters
164
+ - `strings` (`TemplateStringsArray`): The literal parts of the template string.
165
+ - `tokens` (`RGXToken[]`): The RGX tokens to be resolved and concatenated with the literal parts.
166
+
167
+ #### Returns
168
+ - `RegExp`: The resulting regular expression object constructed from the template literal.
169
+
170
+ ## Peer Dependencies
171
+ - `@ptolemy2002/ts-brand-utils` ^1.0.0
172
+ - `@ptolemy2002/ts-utils` ^3.4.0
173
+ - `is-callable` ^1.2.7
174
+
175
+ ## Commands
176
+ The following commands exist in the project:
177
+
178
+ - `npm run uninstall` - Uninstalls all dependencies for the library
179
+ - `npm run reinstall` - Uninstalls and then Reinstalls all dependencies for the library
180
+ - `npm run build` - Builds the library
181
+ - `npm run release` - Publishes the library to npm without changing the version
182
+ - `npm run release-patch` - Publishes the library to npm with a patch version bump
183
+ - `npm run release-minor` - Publishes the library to npm with a minor version bump
184
+ - `npm run release-major` - Publishes the library to npm with a major version bump
185
+ - `npm run test` - Runs the tests for the library
186
+ - `npm run test:watch` - Runs the tests for the library in watch mode
187
+ - `npm run test:coverage` - Runs the tests for the library and generates a coverage report
@@ -0,0 +1,23 @@
1
+ import { Branded } from "@ptolemy2002/ts-brand-utils";
2
+ export type RGXNoOpToken = null | undefined;
3
+ export type RGXNativeToken = string | number | boolean | RGXNoOpToken;
4
+ export type RGXConvertibleToken = {
5
+ toRgx: () => RGXNativeToken | RGXNativeToken[];
6
+ };
7
+ export type RGXToken = RGXNativeToken | RGXConvertibleToken | RGXToken[];
8
+ export type RGXTokenType = 'no-op' | 'native' | 'convertible' | RGXTokenType[];
9
+ export type RGXTokenFromType<T extends RGXTokenType> = T extends 'no-op' ? RGXNoOpToken : T extends 'native' ? RGXNativeToken : T extends 'convertible' ? RGXConvertibleToken : T extends RGXTokenType[] ? {
10
+ [K in keyof T]: T[K] extends RGXTokenType ? RGXTokenFromType<T[K]> : never;
11
+ } : never;
12
+ export declare const validRegexSymbol: unique symbol;
13
+ export type ValidRegexBrandSymbol = typeof validRegexSymbol;
14
+ export type ValidRegexString = Branded<string, [ValidRegexBrandSymbol]>;
15
+ export declare function isRGXNoOpToken(value: unknown): value is RGXNoOpToken;
16
+ export declare function isRGXNativeToken(value: unknown): value is RGXNativeToken;
17
+ export declare function isRGXConvertibleToken(value: unknown): value is RGXConvertibleToken;
18
+ export declare function rgxTokenType(value: RGXToken): RGXTokenType;
19
+ export declare function rgxTokenFromType<T extends RGXTokenType>(type: T, value: RGXToken): RGXTokenFromType<T>;
20
+ export declare function isValidRegex(value: string): value is ValidRegexString;
21
+ export declare function escapeRegex(value: string): ValidRegexString;
22
+ export declare function resolveRGXToken(token: RGXToken): string;
23
+ export default function rgx(strings: TemplateStringsArray, ...tokens: RGXToken[]): RegExp;
package/dist/index.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validRegexSymbol = void 0;
7
+ exports.isRGXNoOpToken = isRGXNoOpToken;
8
+ exports.isRGXNativeToken = isRGXNativeToken;
9
+ exports.isRGXConvertibleToken = isRGXConvertibleToken;
10
+ exports.rgxTokenType = rgxTokenType;
11
+ exports.rgxTokenFromType = rgxTokenFromType;
12
+ exports.isValidRegex = isValidRegex;
13
+ exports.escapeRegex = escapeRegex;
14
+ exports.resolveRGXToken = resolveRGXToken;
15
+ exports.default = rgx;
16
+ const is_callable_1 = __importDefault(require("is-callable"));
17
+ exports.validRegexSymbol = Symbol('ValidRegex');
18
+ function isRGXNoOpToken(value) {
19
+ return value === null || value === undefined;
20
+ }
21
+ function isRGXNativeToken(value) {
22
+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || isRGXNoOpToken(value);
23
+ }
24
+ function isRGXConvertibleToken(value) {
25
+ if (typeof value === 'object' && value !== null && 'toRgx' in value) {
26
+ if ((0, is_callable_1.default)(value.toRgx)) {
27
+ const returnValue = value.toRgx();
28
+ if (Array.isArray(returnValue)) {
29
+ return returnValue.every(isRGXNativeToken);
30
+ }
31
+ return isRGXNativeToken(returnValue);
32
+ }
33
+ return false;
34
+ }
35
+ return false;
36
+ }
37
+ function rgxTokenType(value) {
38
+ if (isRGXNoOpToken(value))
39
+ return 'no-op';
40
+ if (isRGXNativeToken(value))
41
+ return 'native';
42
+ if (isRGXConvertibleToken(value))
43
+ return 'convertible';
44
+ if (Array.isArray(value))
45
+ return value.map(rgxTokenType);
46
+ // Ignoring this line since it should be impossible to reach if the types are correct, but we need it to satisfy the return type
47
+ /* istanbul ignore next */
48
+ throw new TypeError(`Invalid RGX token: ${value}`);
49
+ }
50
+ function rgxTokenFromType(type, value) {
51
+ // Ignoring this line because the function is entirely a TypeScript utility that doesn't need to be tested at runtime.
52
+ /* istanbul ignore next */
53
+ return value;
54
+ }
55
+ function isValidRegex(value) {
56
+ try {
57
+ new RegExp(value);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ function escapeRegex(value) {
65
+ return value.replaceAll(/[\-\^\$.*+?^${}()|[\]\\]/g, '\\$&');
66
+ }
67
+ function resolveRGXToken(token) {
68
+ if (isRGXNoOpToken(token))
69
+ return '';
70
+ if (isRGXNativeToken(token))
71
+ return escapeRegex(String(token));
72
+ if (isRGXConvertibleToken(token)) {
73
+ return resolveRGXToken(token.toRgx());
74
+ }
75
+ // Interpret arrays as unions
76
+ if (Array.isArray(token)) {
77
+ if (token.length === 0)
78
+ return '';
79
+ if (token.length > 1) {
80
+ return '(?:' + token.map(resolveRGXToken).join('|') + ')';
81
+ }
82
+ return resolveRGXToken(token[0]);
83
+ }
84
+ // Ignoring this line since it should be impossible to reach if the types are correct, but we need it to satisfy the return type
85
+ /* istanbul ignore next */
86
+ throw new TypeError(`Invalid RGX token: ${token}`);
87
+ }
88
+ function rgx(strings, ...tokens) {
89
+ let pattern = '';
90
+ const resolvedTokens = tokens.map(resolveRGXToken);
91
+ for (let i = 0; i < strings.length; i++) {
92
+ pattern += strings[i];
93
+ if (i < resolvedTokens.length) {
94
+ pattern += resolvedTokens[i];
95
+ }
96
+ }
97
+ return new RegExp(pattern);
98
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@ptolemy2002/rgx",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/Ptolemy2002/rgx",
13
+ "directory": "lib"
14
+ },
15
+ "scripts": {
16
+ "prepare": "npx ts-patch install -s",
17
+ "build": "bash ./scripts/build.sh",
18
+ "_build": "tsc --project ./tsconfig.build.json",
19
+ "typecheck": "tsc --noEmit",
20
+ "test": "jest",
21
+ "test:watch": "jest --watch",
22
+ "test:coverage": "jest --coverage",
23
+ "postinstall": "npx typesync",
24
+ "uninstall": "bash ./scripts/uninstall.sh",
25
+ "reinstall": "bash ./scripts/reinstall.sh",
26
+ "release": "bash ./scripts/release.sh",
27
+ "release-patch": "bash ./scripts/release.sh patch",
28
+ "release-minor": "bash ./scripts/release.sh minor",
29
+ "release-major": "bash ./scripts/release.sh major"
30
+ },
31
+ "devDependencies": {
32
+ "@ptolemy2002/ts-brand-utils": "^1.0.0",
33
+ "@ptolemy2002/ts-utils": "^3.4.0",
34
+ "@types/is-callable": "^1.1.2",
35
+ "@types/jest": "^29.5.0",
36
+ "is-callable": "^1.2.7",
37
+ "jest": "^29.5.0",
38
+ "ts-jest": "^29.1.0",
39
+ "ts-patch": "^3.3.0",
40
+ "tsconfig-paths": "^4.2.0",
41
+ "typescript-transform-paths": "^3.5.3"
42
+ },
43
+ "peerDependencies": {
44
+ "@ptolemy2002/ts-brand-utils": "^1.0.0",
45
+ "@ptolemy2002/ts-utils": "^3.4.0",
46
+ "is-callable": "^1.2.7"
47
+ }
48
+ }