Skip to content

Commit a84be11

Browse files
committed
feat(regex): EXPORT_AGGREGATE_REGEX
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent 32d154c commit a84be11

File tree

6 files changed

+337
-1
lines changed

6 files changed

+337
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"prettier": "2.8.1",
117117
"prettier-plugin-sh": "0.12.8",
118118
"pretty-format": "29.3.1",
119+
"radash": "10.3.1",
119120
"trash-cli": "5.0.0",
120121
"ts-dedent": "2.2.0",
121122
"ts-node": "10.9.1",
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Vitest Snapshot v1
2+
3+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match named export(s) in multi-line statement 1`] = `
4+
{
5+
"0": "export {
6+
addFive,
7+
addFour,
8+
addThree,
9+
addTwo,
10+
squareFive,
11+
squareFour,
12+
squareThree,
13+
squareTwo
14+
} from './lib'",
15+
"1": undefined,
16+
"2": "{
17+
addFive,
18+
addFour,
19+
addThree,
20+
addTwo,
21+
squareFive,
22+
squareFour,
23+
squareThree,
24+
squareTwo
25+
}",
26+
"3": "./lib",
27+
"groups": {
28+
"exports": "{
29+
addFive,
30+
addFour,
31+
addThree,
32+
addTwo,
33+
squareFive,
34+
squareFour,
35+
squareThree,
36+
squareTwo
37+
}",
38+
"specifier": "./lib",
39+
"type": undefined,
40+
},
41+
"index": 0,
42+
}
43+
`;
44+
45+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match named export(s) in single-line statement 1`] = `
46+
{
47+
"0": "export { defineBuildConfig, type BuildConfig } from \\"#src\\"",
48+
"1": undefined,
49+
"2": "{ defineBuildConfig, type BuildConfig }",
50+
"3": "#src",
51+
"groups": {
52+
"exports": "{ defineBuildConfig, type BuildConfig }",
53+
"specifier": "#src",
54+
"type": undefined,
55+
},
56+
"index": 0,
57+
}
58+
`;
59+
60+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match named type export(s) in multi-line statement 1`] = `
61+
{
62+
"0": "export type {
63+
JsonObject,
64+
LiteralUnion,
65+
Nullable
66+
} from '@flex-development/tutils'",
67+
"1": "type",
68+
"2": "{
69+
JsonObject,
70+
LiteralUnion,
71+
Nullable
72+
}",
73+
"3": "@flex-development/tutils",
74+
"groups": {
75+
"exports": "{
76+
JsonObject,
77+
LiteralUnion,
78+
Nullable
79+
}",
80+
"specifier": "@flex-development/tutils",
81+
"type": "type",
82+
},
83+
"index": 0,
84+
}
85+
`;
86+
87+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match named type export(s) in single-line statement 1`] = `
88+
{
89+
"0": "export type { default as Options } from \\"./options\\"",
90+
"1": "type",
91+
"2": "{ default as Options }",
92+
"3": "./options",
93+
"groups": {
94+
"exports": "{ default as Options }",
95+
"specifier": "./options",
96+
"type": "type",
97+
},
98+
"index": 0,
99+
}
100+
`;
101+
102+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match namespace export 1`] = `
103+
{
104+
"0": "export * from './interfaces'",
105+
"1": undefined,
106+
"2": "*",
107+
"3": "./interfaces",
108+
"groups": {
109+
"exports": "*",
110+
"specifier": "./interfaces",
111+
"type": undefined,
112+
},
113+
"index": 0,
114+
}
115+
`;
116+
117+
exports[`unit:EXPORT_AGGREGATE_REGEX > exports > should match namespace export with alias 1`] = `
118+
{
119+
"0": "export * as constants from \\"./constants\\"",
120+
"1": undefined,
121+
"2": "* as constants",
122+
"3": "./constants",
123+
"groups": {
124+
"exports": "* as constants",
125+
"specifier": "./constants",
126+
"type": undefined,
127+
},
128+
"index": 0,
129+
}
130+
`;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* @file Unit Tests - EXPORT_AGGREGATE_REGEX
3+
* @module export-regex/tests/aggregate/unit
4+
*/
5+
6+
import { omit } from 'radash'
7+
import { dedent } from 'ts-dedent'
8+
import TEST_SUBJECT from '../export-aggregate'
9+
10+
describe('unit:EXPORT_AGGREGATE_REGEX', () => {
11+
beforeEach(() => {
12+
TEST_SUBJECT.lastIndex = 0
13+
})
14+
15+
describe('comments', () => {
16+
it('should ignore export in multi-line comment', () => {
17+
// Arrange
18+
const code = dedent`
19+
/**
20+
* @example
21+
* export * from './utils.mjs'
22+
*/
23+
`
24+
25+
// Act + Expect
26+
expect(TEST_SUBJECT.test(code)).to.be.false
27+
})
28+
29+
it('should ignore export in single-line comment', () => {
30+
expect(TEST_SUBJECT.test('// export { foo } from "./f.mjs"')).to.be.false
31+
})
32+
})
33+
34+
describe('exports', () => {
35+
it('should match named export(s) in multi-line statement', () => {
36+
// Arrange
37+
const code = dedent`
38+
export {
39+
addFive,
40+
addFour,
41+
addThree,
42+
addTwo,
43+
squareFive,
44+
squareFour,
45+
squareThree,
46+
squareTwo
47+
} from './lib'
48+
`
49+
50+
// Act
51+
const result = TEST_SUBJECT.exec(code)
52+
53+
// Expect
54+
expect(result).to.not.be.null
55+
expect(omit(result!, ['input'])).toMatchSnapshot()
56+
})
57+
58+
it('should match named export(s) in single-line statement', () => {
59+
// Arrange
60+
const code = 'export { defineBuildConfig, type BuildConfig } from "#src"'
61+
62+
// Act
63+
const result = TEST_SUBJECT.exec(code)
64+
65+
// Expect
66+
expect(result).to.not.be.null
67+
expect(omit(result!, ['input'])).toMatchSnapshot()
68+
})
69+
70+
it('should match named type export(s) in multi-line statement', () => {
71+
// Arrange
72+
const code = dedent`
73+
export type {
74+
JsonObject,
75+
LiteralUnion,
76+
Nullable
77+
} from '@flex-development/tutils'
78+
`
79+
80+
// Act
81+
const result = TEST_SUBJECT.exec(code)
82+
83+
// Expect
84+
expect(result).to.not.be.null
85+
expect(omit(result!, ['input'])).toMatchSnapshot()
86+
})
87+
88+
it('should match named type export(s) in single-line statement', () => {
89+
// Arrange
90+
const code = 'export type { default as Options } from "./options"'
91+
92+
// Act
93+
const result = TEST_SUBJECT.exec(code)
94+
95+
// Expect
96+
expect(result).to.not.be.null
97+
expect(omit(result!, ['input'])).toMatchSnapshot()
98+
})
99+
100+
it('should match namespace export', () => {
101+
// Act
102+
const result = TEST_SUBJECT.exec(`export * from './interfaces'`)
103+
104+
// Expect
105+
expect(result).to.not.be.null
106+
expect(omit(result!, ['input'])).toMatchSnapshot()
107+
})
108+
109+
it('should match namespace export with alias', () => {
110+
// Arrange
111+
const code = 'export * as constants from "./constants"'
112+
113+
// Act
114+
const result = TEST_SUBJECT.exec(code)
115+
116+
// Expect
117+
expect(result).to.not.be.null
118+
expect(omit(result!, ['input'])).toMatchSnapshot()
119+
})
120+
})
121+
})

src/export-aggregate.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @file EXPORT_AGGREGATE_REGEX
3+
* @module export-regex/aggregate
4+
*/
5+
6+
/**
7+
* Aggregate `export` statement regex. Ignores matches in comments.
8+
*
9+
* @example
10+
* import { EXPORT_AGGREGATE_REGEX } from '@flex-development/export-regex'
11+
* import { dedent } from 'ts-dedent'
12+
*
13+
* const code: string = dedent`
14+
* export { defineBuildConfig, type BuildConfig } from "#src"
15+
* export type {
16+
* JsonObject,
17+
* LiteralUnion,
18+
* Nullable
19+
* } from '@flex-development/tutils'
20+
* export * as constants from "./constants"
21+
* export * from './interfaces'
22+
* `
23+
*
24+
* const print = (matches: IterableIterator<RegExpMatchArray>): void => {
25+
* console.debug([...matches].map(match => omit(match, ['input'])))
26+
* }
27+
*
28+
* print(code.matchAll(EXPORT_AGGREGATE_REGEX))
29+
* // [
30+
* // {
31+
* // '0': 'export { defineBuildConfig, type BuildConfig } from "#src"',
32+
* // '1': undefined,
33+
* // '2': '{ defineBuildConfig, type BuildConfig }',
34+
* // '3': '#src',
35+
* // index: 0,
36+
* // groups: [Object: null prototype] {
37+
* // type: undefined,
38+
* // exports: '{ defineBuildConfig, type BuildConfig }',
39+
* // specifier: '#src'
40+
* // }
41+
* // },
42+
* // {
43+
* // '0': 'export * as constants from "./constants"',
44+
* // '1': undefined,
45+
* // '2': '* as constants',
46+
* // '3': './constants',
47+
* // index: 149,
48+
* // groups: [Object: null prototype] {
49+
* // type: undefined,
50+
* // exports: '* as constants',
51+
* // specifier: './constants'
52+
* // }
53+
* // },
54+
* // {
55+
* // '0': "export * from './interfaces'",
56+
* // '1': undefined,
57+
* // '2': '*',
58+
* // '3': './interfaces',
59+
* // index: 190,
60+
* // groups: [Object: null prototype] {
61+
* // type: undefined,
62+
* // exports: '*',
63+
* // specifier: './interfaces'
64+
* // }
65+
* // }
66+
* // ]
67+
*
68+
* @see https://regex101.com/r/JtvRUt
69+
* @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#re-exporting_aggregating
70+
*
71+
* @const {RegExp} EXPORT_AGGREGATE_REGEX
72+
*/
73+
const EXPORT_AGGREGATE_REGEX: RegExp =
74+
/(?<=^|[\n;])export(?:(?:\s+(?<type>type)\s*)|\s*)(?<exports>(?:\*(?:\s+as\s+\S+)?)|\S+|(?:{[\w\t\n\r "$'*,./{}-]+?}))\s*from\s*["']\s*(?<specifier>(?:(?<='\s*)[^']*[^\s'](?=\s*'))|(?:(?<="\s*)[^"]*[^\s"](?=\s*")))\s*["']/g
75+
76+
export default EXPORT_AGGREGATE_REGEX

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
* @module export-regex
44
*/
55

6-
export {}
6+
export { default as EXPORT_AGGREGATE_REGEX } from './export-aggregate'

yarn.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ __metadata:
10451045
prettier: "npm:2.8.1"
10461046
prettier-plugin-sh: "npm:0.12.8"
10471047
pretty-format: "npm:29.3.1"
1048+
radash: "npm:10.3.1"
10481049
trash-cli: "npm:5.0.0"
10491050
ts-dedent: "npm:2.2.0"
10501051
ts-node: "npm:10.9.1"
@@ -6831,6 +6832,13 @@ __metadata:
68316832
languageName: node
68326833
linkType: hard
68336834

6835+
"radash@npm:10.3.1":
6836+
version: 10.3.1
6837+
resolution: "radash@npm:10.3.1"
6838+
checksum: 2a3eeeba53ed8ae1f08d546dddb6e0d451f30ed0c02d30d8da01e1c56b6b195e558facbc03011db680e41117757bd589d775c2c6f3b3098bae5ba8144a015750
6839+
languageName: node
6840+
linkType: hard
6841+
68346842
"react-is@npm:^18.0.0":
68356843
version: 18.2.0
68366844
resolution: "react-is@npm:18.2.0"

0 commit comments

Comments
 (0)