Skip to content

Commit eb850cf

Browse files
committed
feat(utils): extRegex
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent 60f0209 commit eb850cf

File tree

7 files changed

+164
-0
lines changed

7 files changed

+164
-0
lines changed

__tests__/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
*/
55

66
export type { default as RegExpArray } from './reg-exp-array'
7+
export type { default as Regex } from './regex'

__tests__/types/regex.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @file Test Type Definitions - Regex
3+
* @module tests/types/Regex
4+
*/
5+
6+
/**
7+
* Object containing testable {@linkcode RegExp} properties.
8+
*/
9+
type Regex = Pick<
10+
RegExp,
11+
| 'dotAll'
12+
| 'flags'
13+
| 'global'
14+
| 'ignoreCase'
15+
| 'multiline'
16+
| 'source'
17+
| 'sticky'
18+
| 'unicode'
19+
>
20+
21+
export type { Regex as default }

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
export * from './interfaces'
77
export * from './regex'
8+
export * from './utils'
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @file Type Tests - extRegex
3+
* @module ext-regex/utils/tests/unit-d/extRegex
4+
*/
5+
6+
import type { Options } from '#src/interfaces'
7+
import type testSubject from '../ext-regex'
8+
9+
describe('unit-d:utils/extRegex', () => {
10+
it('should be callable with [string, Options?]', () => {
11+
// Arrange
12+
type Expected = [ext: string, options?: Options | undefined]
13+
14+
// Expect
15+
expectTypeOf<typeof testSubject>().parameters.toEqualTypeOf<Expected>()
16+
})
17+
18+
it('should return RegExp', () => {
19+
expectTypeOf<typeof testSubject>().returns.toEqualTypeOf<RegExp>()
20+
})
21+
})
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @file Unit Tests - extRegex
3+
* @module ext-regex/utils/tests/unit/extRegex
4+
*/
5+
6+
import type { Regex } from '#tests/types'
7+
import * as pathe from '@flex-development/pathe'
8+
import testSubject from '../ext-regex'
9+
10+
describe('unit:utils/extRegex', () => {
11+
it('should return regular expression matching ext', () => {
12+
// Arrange
13+
const cases: [...Parameters<typeof testSubject>, Regex][] = [
14+
[
15+
'.html',
16+
undefined,
17+
{
18+
dotAll: false,
19+
flags: '',
20+
global: false,
21+
ignoreCase: false,
22+
multiline: false,
23+
source: '\\.html(?=\\s*$)',
24+
sticky: false,
25+
unicode: false
26+
}
27+
],
28+
[
29+
'html',
30+
{ flags: 'gi' },
31+
{
32+
dotAll: false,
33+
flags: 'gi',
34+
global: true,
35+
ignoreCase: true,
36+
multiline: false,
37+
source: '\\.html(?=\\s*$)',
38+
sticky: false,
39+
unicode: false
40+
}
41+
]
42+
]
43+
44+
// Act + Expect
45+
cases.forEach(([ext, options, expected]) => {
46+
const result = testSubject(ext, options)
47+
48+
expect(pathe.formatExt(ext)).to.match(result)
49+
expect(result).to.have.property('dotAll').equal(expected.dotAll)
50+
expect(result).to.have.property('flags').equal(expected.flags)
51+
expect(result).to.have.property('ignoreCase').equal(expected.ignoreCase)
52+
expect(result).to.have.property('multiline').equal(expected.multiline)
53+
expect(result).to.have.property('source').equal(expected.source)
54+
expect(result).to.have.property('sticky').equal(expected.sticky)
55+
expect(result).to.have.property('unicode').equal(expected.unicode)
56+
})
57+
})
58+
})

src/utils/ext-regex.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @file Utilities - extRegex
3+
* @module ext-regex/utils/extRegex
4+
*/
5+
6+
import type { Options } from '#src/interfaces'
7+
import * as internal from '#src/internal'
8+
import * as errnode from '@flex-development/errnode'
9+
import * as pathe from '@flex-development/pathe'
10+
import { isNIL, type LiteralUnion } from '@flex-development/tutils'
11+
12+
/**
13+
* Creates a regular expression matching the given file extension, `ext`.
14+
*
15+
* The file extension need not begin with a dot character (`'.'`).
16+
*
17+
* Throws `ERR_INVALID_ARG_TYPE` if the given file extension is not a string.
18+
*
19+
* @see {@linkcode pathe.Ext}
20+
* @see {@linkcode errnode.ERR_INVALID_ARG_TYPE}
21+
*
22+
* @param {LiteralUnion<pathe.Ext, string>} ext - File extension to evaluate
23+
* @param {Options?} [options] - Regular expression options
24+
* @return {RegExp} Regular expression matching `ext`
25+
* @throws {errnode.NodeError<TypeError>} If `ext` is not a string
26+
*/
27+
const extRegex = (
28+
ext: LiteralUnion<pathe.Ext, string>,
29+
options: Options = {}
30+
): RegExp => {
31+
const { flags } = options
32+
33+
// ensure ext is a string
34+
internal.validateString(ext, 'ext')
35+
36+
// ensure flags is a string if defined
37+
!isNIL(flags) && internal.validateString(flags, 'flags')
38+
39+
/**
40+
* Regex pattern for {@linkcode ext}.
41+
*
42+
* Special characters are escaped. A backslash escape (`\\`) is used whenever
43+
* valid. A `\x2d` escape is used when the former would be disallowed by
44+
* stricter Unicode pattern grammar.
45+
*
46+
* @const {string} pattern
47+
*/
48+
const pattern: string = pathe
49+
.formatExt(ext.trim())
50+
.replace(/[$()*+.?[\\\]^{|}]/g, '\\$&')
51+
.replace(/-/g, '\\x2d')
52+
53+
return new RegExp(pattern + '(?=\\s*$)', flags?.trim())
54+
}
55+
56+
export default extRegex

src/utils/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @file Entry Point - Utilities
3+
* @module ext-regex/utils
4+
*/
5+
6+
export { default as extRegex } from './ext-regex'

0 commit comments

Comments
 (0)