Skip to content

Commit 60bffbb

Browse files
committed
chore: wip
1 parent 597c017 commit 60bffbb

File tree

6 files changed

+727
-42
lines changed

6 files changed

+727
-42
lines changed

bin/cli.ts

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
11
import { CAC } from 'cac'
2-
import { faker } from '../src/index'
2+
import { Faker, LocaleLoader } from '../src/index'
33
import { version } from '../package.json'
44

55
const cli = new CAC('nanofaker')
66

77
// Generate command - generate random data
88
cli
99
.command('generate <category> <method>', 'Generate random data')
10-
.option('--locale <locale>', 'Locale to use (en, es, fr, de, it, pt, ja, tl, zh)', { default: 'en' })
10+
.option('--locale <locale>', 'Locale to use', { default: 'en' })
1111
.option('--count <count>', 'Number of items to generate', { default: 1 })
1212
.option('--seed <seed>', 'Seed for reproducible results')
1313
.option('--json', 'Output as JSON')
1414
.example('nanofaker generate person fullName')
1515
.example('nanofaker generate person fullName --locale es --count 5')
1616
.example('nanofaker generate address city --seed 12345 --json')
17-
.action((category: string, method: string, options: any) => {
17+
.action(async (category: string, method: string, options: any) => {
1818
try {
19-
// Set locale
20-
if (options.locale) {
21-
faker.setLocale(options.locale)
22-
}
23-
24-
// Set seed if provided
25-
if (options.seed) {
26-
faker.seed(Number(options.seed))
27-
}
19+
// Create faker with async locale loading
20+
const faker = await Faker.create({
21+
locale: options.locale || 'en',
22+
seed: options.seed ? Number(options.seed) : undefined,
23+
})
2824

2925
// Get the category and method
3026
const fakerCategory = (faker as any)[category]
@@ -112,23 +108,82 @@ cli
112108
// Locales command - list all available locales
113109
cli
114110
.command('locales', 'List all available locales')
115-
.action(() => {
116-
const locales = [
117-
'en - English',
118-
'es - Spanish',
119-
'fr - French',
120-
'de - German',
121-
'it - Italian',
122-
'pt - Portuguese',
123-
'ja - Japanese',
124-
'tl - Filipino',
125-
'zh - Chinese',
126-
]
111+
.option('--loaded', 'Show only loaded locales')
112+
.action((options: any) => {
113+
if (options.loaded) {
114+
const availableLocales = Faker.availableLocales
115+
const loadedLocales = availableLocales.filter(locale => Faker.isLocaleLoaded(locale))
116+
117+
console.log('\nLoaded Locales:\n')
118+
if (loadedLocales.length === 0) {
119+
console.log(' No locales loaded yet')
120+
}
121+
else {
122+
loadedLocales.forEach(locale => console.log(` ✓ ${locale}`))
123+
}
124+
}
125+
else {
126+
const locales = [
127+
'en - English',
128+
'es - Spanish',
129+
'fr - French',
130+
'de - German',
131+
'it - Italian',
132+
'pt - Portuguese',
133+
'ja - Japanese',
134+
'tl - Filipino',
135+
'zh - Chinese',
136+
'nl - Dutch',
137+
'ko - Korean',
138+
'no - Norwegian',
139+
'sv - Swedish',
140+
'da - Danish',
141+
'uk - Ukrainian',
142+
'hi - Hindi',
143+
'fi - Finnish',
144+
'tr - Turkish',
145+
'pl - Polish',
146+
'cs - Czech',
147+
]
127148

128-
console.log('\nAvailable Locales:\n')
129-
locales.forEach(locale => console.log(` ${locale}`))
130-
console.log('\nUse --locale <code> with the generate command to use a specific locale')
131-
console.log('Example: nanofaker generate person fullName --locale es')
149+
console.log('\nAvailable Locales (20 total):\n')
150+
locales.forEach(locale => console.log(` ${locale}`))
151+
console.log('\nℹ️ Only English (en) is bundled by default.')
152+
console.log(' Other locales are loaded dynamically when first used.')
153+
console.log('\nUse --locale <code> with the generate command')
154+
console.log('Example: nanofaker generate person fullName --locale es')
155+
}
156+
})
157+
158+
// Preload locales command
159+
cli
160+
.command('locales:preload [locales...]', 'Preload specific locales for faster access')
161+
.example('nanofaker locales:preload es fr de')
162+
.example('nanofaker locales:preload all')
163+
.action(async (locales: string[]) => {
164+
try {
165+
const localesToLoad = locales.includes('all')
166+
? Faker.availableLocales.filter(l => l !== 'en')
167+
: locales
168+
169+
if (localesToLoad.length === 0) {
170+
console.error('Error: Please specify locales to preload or use "all"')
171+
console.log('\nExample: nanofaker locales:preload es fr de')
172+
console.log('Example: nanofaker locales:preload all')
173+
process.exit(1)
174+
}
175+
176+
console.log(`\nPreloading ${localesToLoad.length} locale(s)...`)
177+
178+
await Faker.preloadLocales(localesToLoad as string[])
179+
180+
console.log('✓ Locales preloaded successfully:')
181+
localesToLoad.forEach(locale => console.log(` ✓ ${locale}`))
182+
}
183+
catch (error) {
184+
console.error('Error:', error instanceof Error ? error.message : error)
185+
process.exit(1)
186+
}
132187
})
133188

134189
// Batch command - generate multiple records with multiple fields

docs/comparison.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ How nanofaker compares to other popular faker libraries.
99
| **Bundle Size** | ~50KB | ~200-500KB | N/A (PHP) | ~100KB | ~80KB |
1010
| **Performance** | ⚡️ Fastest | Medium | N/A | Medium | Medium |
1111
| **TypeScript** | ✅ Native | ✅ Yes | ❌ No (PHP) | ⚠️ Types available | ⚠️ Types available |
12-
| **Locales** | 16 (complete) | 70+ (partial) | 50+ | Limited | Limited |
12+
| **Locales** | 20 (complete) | 70+ (partial) | 50+ | Limited | Limited |
1313
| **Locale Coverage** | 100% | 30-80% | Varies | N/A | N/A |
1414
| **CLI Tool** | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No |
1515
| **Tree Shaking** | ✅ Yes | ⚠️ Partial | N/A | ❌ No | ❌ No |

src/faker.ts

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { SportModule } from './modules/sport'
2626
import { HackerModule } from './modules/hacker'
2727
import { SystemModule } from './modules/system'
2828
import { WordModule } from './modules/word'
29-
import { locales } from './locales'
29+
import { LocaleLoader } from './locale-loader'
3030

3131
/**
3232
* Main Faker class - the entry point for generating fake data
@@ -75,7 +75,17 @@ export class Faker {
7575
const locale = options?.locale ?? 'en'
7676
const seed = options?.seed
7777

78-
this._locale = locales[locale] ?? locales.en
78+
// For non-English locales, warn about async loading
79+
if (locale !== 'en' && !LocaleLoader.isCached(locale)) {
80+
console.warn(
81+
`[nanofaker] Locale '${locale}' requires async loading. Use 'await Faker.create({ locale: "${locale}" })' for better performance. Falling back to English.`,
82+
)
83+
this._locale = LocaleLoader.loadSync('en')
84+
}
85+
else {
86+
this._locale = LocaleLoader.loadSync(locale)
87+
}
88+
7989
this._random = new Random(seed)
8090

8191
// Initialize all modules
@@ -107,6 +117,21 @@ export class Faker {
107117
this.vehicle = new VehicleModule(this._random, this._locale)
108118
}
109119

120+
/**
121+
* Create a Faker instance with async locale loading
122+
* @example await Faker.create({ locale: 'es' })
123+
*/
124+
static async create(options?: FakerOptions): Promise<Faker> {
125+
const locale = options?.locale ?? 'en'
126+
127+
// Preload the locale if needed
128+
if (!LocaleLoader.isCached(locale)) {
129+
await LocaleLoader.load(locale)
130+
}
131+
132+
return new Faker(options)
133+
}
134+
110135
/**
111136
* Set a seed for reproducible results
112137
* @example faker.seed(12345)
@@ -146,13 +171,50 @@ export class Faker {
146171
}
147172

148173
/**
149-
* Set the locale
150-
* @example faker.setLocale('es')
174+
* Set the locale asynchronously (recommended for non-English locales)
175+
* @example await faker.setLocale('es')
151176
*/
152-
setLocale(locale: string): this {
153-
this._locale = locales[locale] ?? locales.en
177+
async setLocale(locale: string): Promise<this> {
178+
this._locale = await LocaleLoader.load(locale)
154179

155180
// Reinitialize modules that depend on locale
181+
this.reinitializeLocaleModules()
182+
183+
return this
184+
}
185+
186+
/**
187+
* Set the locale synchronously (only works for cached locales)
188+
* @deprecated Use async setLocale() instead
189+
* @example faker.setLocaleSync('es') // Only works if 'es' is already loaded
190+
*/
191+
setLocaleSync(locale: string): this {
192+
if (!LocaleLoader.isCached(locale) && locale !== 'en') {
193+
console.warn(
194+
`[nanofaker] Locale '${locale}' is not loaded. Use 'await faker.setLocale("${locale}")' or preload it first.`,
195+
)
196+
return this
197+
}
198+
199+
this._locale = LocaleLoader.loadSync(locale)
200+
this.reinitializeLocaleModules()
201+
202+
return this
203+
}
204+
205+
/**
206+
* Preload locales for offline usage
207+
* @example await faker.preloadLocales(['es', 'fr', 'de'])
208+
*/
209+
async preloadLocales(locales: string[]): Promise<this> {
210+
await LocaleLoader.preload(locales)
211+
return this
212+
}
213+
214+
/**
215+
* Reinitialize all modules that depend on locale
216+
*/
217+
private reinitializeLocaleModules(): void {
156218
Object.assign(this.person, new PersonModule(this._random, this._locale))
157219
Object.assign(this.address, new AddressModule(this._random, this._locale))
158220
Object.assign(this.internet, new InternetModule(this._random, this._locale))
@@ -170,8 +232,6 @@ export class Faker {
170232
Object.assign(this.system, new SystemModule(this._random, this._locale))
171233
Object.assign(this.color, new ColorModule(this._random, this._locale))
172234
Object.assign(this.science, new ScienceModule(this._random, this._locale))
173-
174-
return this
175235
}
176236

177237
/**
@@ -184,8 +244,23 @@ export class Faker {
184244
/**
185245
* Get available locales
186246
*/
187-
static get availableLocales(): string[] {
188-
return Object.keys(locales)
247+
static get availableLocales(): readonly string[] {
248+
return LocaleLoader.getAvailableLocales()
249+
}
250+
251+
/**
252+
* Check if a locale is currently loaded/cached
253+
*/
254+
static isLocaleLoaded(locale: string): boolean {
255+
return LocaleLoader.isCached(locale)
256+
}
257+
258+
/**
259+
* Preload multiple locales statically
260+
* @example await Faker.preloadLocales(['es', 'fr', 'de'])
261+
*/
262+
static async preloadLocales(locales: string[]): Promise<void> {
263+
await LocaleLoader.preload(locales)
189264
}
190265

191266
/**

src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ export * from './config'
22
export * from './types'
33
export * from './faker'
44
export * from './random'
5-
export { locales, en, es, fr, de, it, pt, ja, tl } from './locales'
5+
export * from './locale-loader'
6+
export { en } from './locales/en'
67

7-
// Re-export modules
88
export { PersonModule } from './modules/person'
99
export { AddressModule } from './modules/address'
1010
export { InternetModule } from './modules/internet'
@@ -34,7 +34,7 @@ export { WordModule } from './modules/word'
3434

3535
// Default faker instance for convenience
3636
import { Faker } from './faker'
37-
export const faker = new Faker()
37+
export const faker: Faker = new Faker()
3838

3939
// Default export
4040
export default faker

0 commit comments

Comments
 (0)