Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New util findAllWithRegex #19

Merged
merged 2 commits into from
Dec 20, 2023
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
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 18.x
- name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20.x
- run: yarn
- name: Build the typescript code
run: yarn build
Expand All @@ -23,7 +23,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node-version: [18.x, 18.x]
node-version: [20.x, 20.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -37,10 +37,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 18.x
- name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20.x
- run: yarn
- run: yarn depcheck

Expand All @@ -51,10 +51,10 @@ jobs:
with:
fetch-depth: 100 # need the history to do a changed files check below (source, origin)
- uses: actions/setup-python@v2
- name: Use Node.js 18.x
- name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20.x
- run: yarn
- uses: pre-commit/[email protected]
with:
Expand All @@ -73,7 +73,7 @@ jobs:
uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '18.x'
node-version: '20.x'
- run: yarn
- name: Configure NPM authentication
run: |
Expand All @@ -95,7 +95,7 @@ jobs:
uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '18.x'
node-version: '20.x'
- run: yarn
- name: Configure Github Packages authentication
run: |
Expand Down
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';
Loading