Skip to content

Commit f8108d4

Browse files
committed
chore: wip
1 parent ac6bc7c commit f8108d4

File tree

5 files changed

+112
-45
lines changed

5 files changed

+112
-45
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,9 @@ nanofaker is designed for performance and efficiency:
339339

340340
**Performance:**
341341
-**9 out of 9 benchmarks won** (100% win rate!)
342-
-**6.92x faster than @faker-js/faker** on average
343-
- 🚀 **33.56M ops/s** for city generation
344-
- 📊 **8.26ms** to generate 10,000 complex user objects
342+
-**17.50x faster than @faker-js/faker** on average
343+
- 🚀 **32.93M ops/s** for country generation
344+
- 📊 **5.50ms** to generate 10,000 complex user objects
345345

346346
**Package Size:**
347347
- 📦 **174 KB** published (core package only)
@@ -353,11 +353,11 @@ nanofaker is designed for performance and efficiency:
353353

354354
| Operation | nanofaker | @faker-js/faker | Speedup |
355355
|-----------|-----------|-----------------|---------|
356-
| Full Name Generation | 24.31M ops/s | 663.17K ops/s | **36.7x faster** |
357-
| Phone Number Generation | 11.79M ops/s | 2.63M ops/s | **4.5x faster** |
358-
| City Generation | 33.56M ops/s | 1.08M ops/s | **31.1x faster** |
359-
| Email Generation | 2.49M ops/s | 647.77K ops/s | **3.8x faster** |
360-
| Complex Objects (10k) | 8.26ms | 57.16ms | **6.9x faster** |
356+
| Full Name Generation | 24.25M ops/s | 687.19K ops/s | **35.3x faster** |
357+
| Email Generation | 6.68M ops/s | 667.60K ops/s | **10.0x faster** |
358+
| UUID Generation | 6.84M ops/s | 501.99K ops/s | **13.6x faster** |
359+
| City Generation | 31.23M ops/s | 1.02M ops/s | **30.6x faster** |
360+
| Complex Objects (10k) | 5.50ms | 96.36ms | **17.5x faster** |
361361

362362
### Running Benchmarks
363363

packages/benchmarks/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Comprehensive benchmarks comparing nanofaker against other popular JavaScript/Ty
1515
### Key Advantages
1616

1717
**nanofaker offers:**
18-
-**Fastest performance** - 6.92x faster than @faker-js/faker
18+
-**Fastest performance** - 17.50x faster than @faker-js/faker
1919
-**Smallest size** - 24.7x smaller than @faker-js/faker (4.1 MB savings)
2020
-**Fewest files** - 6.1x fewer files than @faker-js/faker
2121
-**Complete locale coverage** - 100% for all 26 languages

packages/benchmarks/src/index.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async function runBenchmarks() {
7070
console.log(colors.reset)
7171

7272
// Benchmark 1: Full Name Generation
73-
const nameBench = new Bench({ time: 500 })
73+
const nameBench = new Bench({ time: 750 })
7474
nameBench
7575
.add('nanofaker', () => {
7676
nanofaker.person.fullName()
@@ -92,7 +92,7 @@ async function runBenchmarks() {
9292
printResults(nameBench, '📛 Full Name Generation')
9393

9494
// Benchmark 2: Email Generation
95-
const emailBench = new Bench({ time: 500 })
95+
const emailBench = new Bench({ time: 750 })
9696
emailBench
9797
.add('nanofaker', () => {
9898
nanofaker.internet.email()
@@ -114,7 +114,7 @@ async function runBenchmarks() {
114114
printResults(emailBench, '📧 Email Generation')
115115

116116
// Benchmark 3: Phone Number Generation
117-
const phoneBench = new Bench({ time: 500 })
117+
const phoneBench = new Bench({ time: 750 })
118118
phoneBench
119119
.add('nanofaker', () => {
120120
nanofaker.phone.number()
@@ -136,7 +136,7 @@ async function runBenchmarks() {
136136
printResults(phoneBench, '📞 Phone Number Generation')
137137

138138
// Benchmark 4: City Generation
139-
const cityBench = new Bench({ time: 500 })
139+
const cityBench = new Bench({ time: 750 })
140140
cityBench
141141
.add('nanofaker', () => {
142142
nanofaker.address.city()
@@ -158,7 +158,7 @@ async function runBenchmarks() {
158158
printResults(cityBench, '🏙️ City Generation')
159159

160160
// Benchmark 5: Country Generation
161-
const countryBench = new Bench({ time: 500 })
161+
const countryBench = new Bench({ time: 750 })
162162
countryBench
163163
.add('nanofaker', () => {
164164
nanofaker.address.country()
@@ -180,7 +180,7 @@ async function runBenchmarks() {
180180
printResults(countryBench, '🌍 Country Generation')
181181

182182
// Benchmark 6: Company Name Generation
183-
const companyBench = new Bench({ time: 500 })
183+
const companyBench = new Bench({ time: 750 })
184184
companyBench
185185
.add('nanofaker', () => {
186186
nanofaker.company.name()
@@ -202,7 +202,7 @@ async function runBenchmarks() {
202202
printResults(companyBench, '🏢 Company Name Generation')
203203

204204
// Benchmark 7: Product Generation
205-
const productBench = new Bench({ time: 500 })
205+
const productBench = new Bench({ time: 750 })
206206
productBench
207207
.add('nanofaker', () => {
208208
nanofaker.commerce.product()
@@ -221,7 +221,7 @@ async function runBenchmarks() {
221221
printResults(productBench, '🛍️ Product Generation')
222222

223223
// Benchmark 8: UUID Generation
224-
const uuidBench = new Bench({ time: 500 })
224+
const uuidBench = new Bench({ time: 750 })
225225
uuidBench
226226
.add('nanofaker', () => {
227227
nanofaker.string.uuid()

packages/core/src/modules/internet.ts

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,29 @@ export class InternetModule {
1212
* @example faker.internet.email() // 'john.doe@example.com'
1313
*/
1414
email(options?: { firstName?: string, lastName?: string, provider?: string }): string {
15-
// Use all gendered names combined for email generation
16-
const allFirstNames = [
17-
...this.locale.person.firstNameMale,
18-
...this.locale.person.firstNameFemale,
19-
...(this.locale.person.firstNameNeutral || []),
20-
]
21-
const firstName = options?.firstName ?? this.random.arrayElement(allFirstNames).toLowerCase()
15+
// Optimized: avoid array spreading by selecting from combined length
16+
let firstName: string
17+
if (options?.firstName) {
18+
firstName = options.firstName
19+
}
20+
else {
21+
const maleLen = this.locale.person.firstNameMale.length
22+
const femaleLen = this.locale.person.firstNameFemale.length
23+
const neutralLen = this.locale.person.firstNameNeutral?.length || 0
24+
const totalLen = maleLen + femaleLen + neutralLen
25+
const index = this.random.int(0, totalLen - 1)
26+
27+
if (index < maleLen) {
28+
firstName = this.locale.person.firstNameMale[index].toLowerCase()
29+
}
30+
else if (index < maleLen + femaleLen) {
31+
firstName = this.locale.person.firstNameFemale[index - maleLen].toLowerCase()
32+
}
33+
else {
34+
firstName = this.locale.person.firstNameNeutral![index - maleLen - femaleLen].toLowerCase()
35+
}
36+
}
37+
2238
const lastName = options?.lastName ?? this.random.arrayElement(this.locale.person.lastName).toLowerCase()
2339
const provider = options?.provider ?? this.random.arrayElement(this.locale.internet.domainSuffix ?? ['com', 'net', 'org'])
2440

@@ -41,13 +57,24 @@ export class InternetModule {
4157
* @example faker.internet.freeEmail() // 'john.doe@gmail.com'
4258
*/
4359
freeEmail(): string {
44-
// Use all gendered names combined for email generation
45-
const allFirstNames = [
46-
...this.locale.person.firstNameMale,
47-
...this.locale.person.firstNameFemale,
48-
...(this.locale.person.firstNameNeutral || []),
49-
]
50-
const firstName = this.random.arrayElement(allFirstNames).toLowerCase()
60+
// Optimized: avoid array spreading by selecting from combined length
61+
const maleLen = this.locale.person.firstNameMale.length
62+
const femaleLen = this.locale.person.firstNameFemale.length
63+
const neutralLen = this.locale.person.firstNameNeutral?.length || 0
64+
const totalLen = maleLen + femaleLen + neutralLen
65+
const index = this.random.int(0, totalLen - 1)
66+
67+
let firstName: string
68+
if (index < maleLen) {
69+
firstName = this.locale.person.firstNameMale[index].toLowerCase()
70+
}
71+
else if (index < maleLen + femaleLen) {
72+
firstName = this.locale.person.firstNameFemale[index - maleLen].toLowerCase()
73+
}
74+
else {
75+
firstName = this.locale.person.firstNameNeutral![index - maleLen - femaleLen].toLowerCase()
76+
}
77+
5178
const lastName = this.random.arrayElement(this.locale.person.lastName).toLowerCase()
5279
const provider = this.random.arrayElement(this.locale.internet.freeEmail ?? ['gmail.com', 'yahoo.com', 'hotmail.com'])
5380

@@ -62,14 +89,30 @@ export class InternetModule {
6289
* @example faker.internet.username() // 'john_doe123'
6390
*/
6491
username(options?: { firstName?: string, lastName?: string }): string {
65-
// Use all gendered names combined for username generation
66-
const allFirstNames = [
67-
...this.locale.person.firstNameMale,
68-
...this.locale.person.firstNameFemale,
69-
...(this.locale.person.firstNameNeutral || []),
70-
]
71-
const firstName = options?.firstName ?? this.random.arrayElement(allFirstNames).toLowerCase()
72-
const lastName = options?.lastName ?? this.random.arrayElement(this.locale.person.lastName).toLowerCase()
92+
// Optimized: avoid array spreading by selecting from combined length
93+
let firstName: string
94+
if (options?.firstName) {
95+
firstName = options.firstName.toLowerCase()
96+
}
97+
else {
98+
const maleLen = this.locale.person.firstNameMale.length
99+
const femaleLen = this.locale.person.firstNameFemale.length
100+
const neutralLen = this.locale.person.firstNameNeutral?.length || 0
101+
const totalLen = maleLen + femaleLen + neutralLen
102+
const index = this.random.int(0, totalLen - 1)
103+
104+
if (index < maleLen) {
105+
firstName = this.locale.person.firstNameMale[index].toLowerCase()
106+
}
107+
else if (index < maleLen + femaleLen) {
108+
firstName = this.locale.person.firstNameFemale[index - maleLen].toLowerCase()
109+
}
110+
else {
111+
firstName = this.locale.person.firstNameNeutral![index - maleLen - femaleLen].toLowerCase()
112+
}
113+
}
114+
115+
const lastName = options?.lastName?.toLowerCase() ?? this.random.arrayElement(this.locale.person.lastName).toLowerCase()
73116

74117
const patterns = [
75118
`${firstName}${lastName}`,

packages/core/src/modules/string.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,27 @@ export class StringModule {
99
* @example faker.string.uuid() // '550e8400-e29b-41d4-a716-446655440000'
1010
*/
1111
uuid(): string {
12-
const template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
13-
return template.replace(/[xy]/g, (c) => {
14-
const r = this.random.int(0, 15)
15-
const v = c === 'x' ? r : (r & 0x3 | 0x8)
16-
return v.toString(16)
17-
})
12+
// Optimized: avoid regex overhead with direct string building
13+
const hex = '0123456789abcdef'
14+
let result = ''
15+
16+
for (let i = 0; i < 36; i++) {
17+
if (i === 8 || i === 13 || i === 18 || i === 23) {
18+
result += '-'
19+
}
20+
else if (i === 14) {
21+
result += '4'
22+
}
23+
else if (i === 19) {
24+
const r = this.random.int(0, 15)
25+
result += hex[(r & 0x3) | 0x8]
26+
}
27+
else {
28+
result += hex[this.random.int(0, 15)]
29+
}
30+
}
31+
32+
return result
1833
}
1934

2035
/**
@@ -94,6 +109,15 @@ export class StringModule {
94109
* @example faker.string.numeric() // '1234567890'
95110
*/
96111
numeric(length = 10): string {
112+
// Optimized: use direct Math.random for unseeded case
113+
if (this.random['seed'] === undefined) {
114+
let result = ''
115+
for (let i = 0; i < length; i++) {
116+
result += Math.floor(Math.random() * 10)
117+
}
118+
return result
119+
}
120+
97121
let result = ''
98122
for (let i = 0; i < length; i++) {
99123
result += this.random.int(0, 9)

0 commit comments

Comments
 (0)