Skip to content

Commit

Permalink
New util findAllWithRegex
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfarrell76 committed Dec 20, 2023
1 parent cf0bdd9 commit 302e201
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"**/.pnp.*": true
},
"editor.codeActionsOnSave": {
"source.fixAll": true
"source.fixAll": "explicit"
},
"cSpell.words": [
"camelcase",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"author": "Transcend Inc.",
"name": "@transcend-io/type-utils",
"description": "Small package containing useful typescript utilities.",
"version": "1.2.2",
"version": "1.3.0",
"homepage": "https://github.com/transcend-io/type-utils",
"repository": {
"type": "git",
Expand Down
92 changes: 92 additions & 0 deletions src/findAllWithRegex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* The full match returned when regex.exec finds a match
*/
export interface RegExpMatch {
/** The full regex match */
fullMatch: string;
/** The index in the text where the match was found */
matchIndex: number;
/** When a strict regex is provided, check if the regex matches the strict standard */
isStrict?: boolean;
}

/**
* Use a regex with the 'g' global flag to list all items in a file. Can then map the matches back to object parameters
*/
export interface FindAllRegExp<TMatchKeys extends string> {
/** The regex to test */
value: RegExp;
/**
* A RegExp that is stricter than `value` and can be used to test if the definition matches a certain standard.
*
* i.e. I may have a loose match for jsdoc properties but I want a stricter standard for linting them
*/
strict?: RegExp;
/**
* The match groups in the regex will map to these object attributes.
* If there are more match groups than matches, an error is thrown
*/
matches: TMatchKeys[];
/** When true, skip validation of matches being the expected length */
skipMatchValidation?: boolean;
}

/**
* Use a regex with the global flag to find all matches and return the list of matches with match groups mapped to parameter names
* @param regex - The regex definition and match attributes
* @param text - The text to find all in
* @returns The matches all converted to type T
*/
export function findAllWithRegex<TMatchKeys extends string>(
regex: FindAllRegExp<TMatchKeys>,
text: string,
): ({ [k in TMatchKeys]: string } & RegExpMatch)[] {
// Validate the parameters
if (!regex.value.flags.includes('g')) {
throw new Error('Regex.value must have a g flag');
}

// The name of the match groups
const matchParams = ['fullMatch', ...regex.matches];

// The results
const results: ({ [k in TMatchKeys]: string } & RegExpMatch)[] = [];

// Check for initial match
let match = regex.value.exec(text);
let i = 0;

// Loop over all matches
while (match) {
// Ensure that each match is accounted for
if (matchParams.length !== match.length && !regex.skipMatchValidation) {
throw new Error(
`Mismatch in match length at index [${i}]: "${match.length}" vs expected: "${matchParams.length}"`,
);
}

// Construct the match result and assert it is of type T & RegExpMatch
const result = match.reduce(
(acc, matchResult, ind) =>
Object.assign(acc, { [matchParams[ind]]: matchResult }),
{},
) as { [k in TMatchKeys]: string } & RegExpMatch;

// Save the index
result.matchIndex = match.index;

// Test if the strict regex passes
if (regex.strict) {
result.isStrict = regex.strict.test(result.fullMatch);
}

// Save the result
results.push(result);

// Check for another
match = regex.value.exec(text);
i += 1;
}

return results;
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './gql';
export * from './invert';
export * from './types';
export * from './valuesOf';
export * from './findAllWithRegex';

0 comments on commit 302e201

Please sign in to comment.