Skip to content

Commit 419e9e2

Browse files
authored
feat(types): provide strong typing for locales 2 (#398)
1 parent 6afa92b commit 419e9e2

382 files changed

Lines changed: 2572 additions & 641 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/generateLocales.ts

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import type { Options } from 'prettier';
44
import { format } from 'prettier';
55
import options from '../.prettierrc.cjs';
66
import type { LocaleDefinition } from '../src';
7+
import { DEFINITIONS } from '../src/definitions';
8+
9+
// Constants
710

811
const pathRoot = resolve(__dirname, '..');
912
const pathLocale = resolve(pathRoot, 'src', 'locale');
@@ -16,36 +19,58 @@ const pathDocsApiLocalization = resolve(
1619
'localization.md'
1720
);
1821

19-
const scriptCommand = 'pnpm run generate:locales';
2022
const prettierTsOptions: Options = { ...options, parser: 'typescript' };
2123
const prettierMdOptions: Options = { ...options, parser: 'markdown' };
2224

23-
const locales = readdirSync(pathLocales);
24-
locales.splice(locales.indexOf('index.ts'), 1);
25-
26-
let localeIndexImports = "import type { LocaleDefinition } from '..';\n";
27-
let localeIndexType = 'export type KnownLocale =\n';
28-
let localeIndexLocales = 'const locales: KnownLocales = {\n';
29-
30-
let localizationLocales = '| Locale | Name |\n| :--- | :--- |\n';
25+
const scriptCommand = 'pnpm run generate:locales';
3126

3227
const autoGeneratedCommentHeader = `/*
3328
* This file is automatically generated.
3429
* Run '${scriptCommand}' to update.
3530
*/`;
3631

37-
for (const locale of locales) {
38-
// eslint-disable-next-line @typescript-eslint/no-var-requires
39-
const localeDef: LocaleDefinition = require('../src/locales/' +
40-
locale).default;
41-
const localeTitle = localeDef.title;
32+
// Helper functions
4233

43-
localeIndexImports += `import ${locale} from './${locale}';\n`;
44-
localeIndexType += ` | '${locale}'\n`;
45-
localeIndexLocales += ` ${locale},\n`;
46-
localizationLocales += `| ${locale} | ${localeTitle} |\n`;
34+
function removeIndexTs(files: string[]): string[] {
35+
const index = files.indexOf('index.ts');
36+
if (index !== -1) {
37+
files.splice(index, 1);
38+
}
39+
return files;
40+
}
4741

48-
// src/locale/<locale>.ts
42+
function removeTsSuffix(files: string[]): string[] {
43+
return files.map((file) => file.replace('.ts', ''));
44+
}
45+
46+
function escapeImport(module: string): string {
47+
if (module === 'name') {
48+
return 'name_';
49+
} else if (module === 'type') {
50+
return 'type_';
51+
} else {
52+
return module;
53+
}
54+
}
55+
56+
function escapeField(module: string): string {
57+
if (module === 'name') {
58+
return 'name: name_';
59+
} else if (module === 'type') {
60+
return 'type: type_';
61+
} else {
62+
return module;
63+
}
64+
}
65+
66+
function containsAll(checked?: string[], expected?: string[]): boolean {
67+
if (typeof expected === 'undefined' || typeof checked === 'undefined') {
68+
return true;
69+
}
70+
return expected.every((c) => checked.includes(c));
71+
}
72+
73+
function generateLocaleFile(locale: string) {
4974
let content = `
5075
${autoGeneratedCommentHeader}
5176
@@ -69,6 +94,106 @@ for (const locale of locales) {
6994
writeFileSync(resolve(pathLocale, locale + '.ts'), content);
7095
}
7196

97+
function generateLocalesIndexFile(
98+
path: string,
99+
name: string,
100+
type: string,
101+
depth: number,
102+
extra: string = '',
103+
expected?: string[]
104+
) {
105+
let modules = readdirSync(path);
106+
modules = removeIndexTs(modules);
107+
modules = removeTsSuffix(modules);
108+
const importType = type;
109+
if (!containsAll(modules, expected)) {
110+
type = `Partial<${type}>`;
111+
}
112+
let fieldType = '';
113+
let asType = '';
114+
if (!containsAll(expected, modules)) {
115+
asType = ` as ${type}`;
116+
} else {
117+
fieldType = `: ${type}`;
118+
}
119+
let content = `${autoGeneratedCommentHeader}
120+
import type { ${importType} } from '..${'/..'.repeat(depth)}';
121+
${modules
122+
.map((module) => `import ${escapeImport(module)} from './${module}';`)
123+
.join('\n')}
124+
125+
const ${name}${fieldType} = {
126+
${extra}
127+
${modules.map((module) => `${escapeField(module)},`).join('\n')}
128+
}${asType};
129+
130+
export default ${name};
131+
`;
132+
content = format(content, prettierTsOptions);
133+
writeFileSync(resolve(path, 'index.ts'), content);
134+
}
135+
136+
// Start of actual logic
137+
138+
const locales = readdirSync(pathLocales);
139+
removeIndexTs(locales);
140+
141+
let localeIndexImports = "import type { LocaleDefinition } from '..';\n";
142+
let localeIndexType = 'export type KnownLocale =\n';
143+
let localeIndexLocales = 'const locales: KnownLocales = {\n';
144+
145+
let localizationLocales = '| Locale | Name |\n| :--- | :--- |\n';
146+
147+
for (const locale of locales) {
148+
// eslint-disable-next-line @typescript-eslint/no-var-requires
149+
const localeDef: LocaleDefinition = require('../src/locales/' +
150+
locale).default;
151+
const localeTitle = localeDef.title;
152+
const localeSeparator = localeDef.separator;
153+
154+
localeIndexImports += `import ${locale} from './${locale}';\n`;
155+
localeIndexType += ` | '${locale}'\n`;
156+
localeIndexLocales += ` ${locale},\n`;
157+
localizationLocales += `| ${locale} | ${localeTitle} |\n`;
158+
159+
// src/locale/<locale>.ts
160+
generateLocaleFile(locale);
161+
162+
// src/locales/<locale>/index.ts
163+
const pathModules = resolve(pathLocales, locale);
164+
generateLocalesIndexFile(
165+
pathModules,
166+
locale,
167+
'LocaleDefinition',
168+
1,
169+
`title: '${localeTitle}',` +
170+
(localeSeparator ? `\nseparator: '${localeSeparator}',` : ''),
171+
undefined
172+
);
173+
174+
let modules = readdirSync(pathModules);
175+
modules = removeIndexTs(modules);
176+
modules = removeTsSuffix(modules);
177+
for (const module of modules) {
178+
// src/locales/<locale>/<module>/index.ts
179+
const pathModule = resolve(pathModules, module);
180+
const moduleFiles: string[] = DEFINITIONS[module];
181+
if (typeof moduleFiles === 'undefined') {
182+
continue;
183+
}
184+
generateLocalesIndexFile(
185+
pathModule,
186+
module,
187+
`${module.replace(/(^|_)([a-z])/g, (s) =>
188+
s.replace('_', '').toUpperCase()
189+
)}Definitions`,
190+
2,
191+
'',
192+
moduleFiles
193+
);
194+
}
195+
}
196+
72197
// src/locales/index.ts
73198

74199
let indexContent = `
@@ -86,7 +211,6 @@ let indexContent = `
86211
`;
87212

88213
indexContent = format(indexContent, prettierTsOptions);
89-
90214
writeFileSync(pathLocalesIndex, indexContent);
91215

92216
// docs/api/localization.md

src/index.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ import { Database } from './database';
66
import { Datatype } from './datatype';
77
import { _Date } from './date';
88
import type { LocaleDefinition } from './definitions';
9+
export type {
10+
AddressDefinitions,
11+
AnimalDefinitions,
12+
CommerceDefinitions,
13+
CommerceProductNameDefinitions,
14+
CompanyDefinitions,
15+
DatabaseDefinitions,
16+
DateDefinitions,
17+
DateEntryDefinition,
18+
DefinitionTypes,
19+
FinanceCurrencyEntryDefinitions,
20+
FinanceDefinitions,
21+
HackerDefinitions,
22+
InternetDefinitions,
23+
LocaleDefinition,
24+
LoremDefinitions,
25+
MusicDefinitions,
26+
NameDefinitions,
27+
NameTitleDefinitions,
28+
PhoneNumberDefinitions,
29+
SystemDefinitions,
30+
SystemMimeTypeEntryDefinitions,
31+
VehicleDefinitions,
32+
WordDefinitions,
33+
} from './definitions';
934
import { DEFINITIONS } from './definitions';
1035
import { Fake } from './fake';
1136
import { Finance } from './finance';
@@ -34,8 +59,6 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
3459
export type UsableLocale = LiteralUnion<KnownLocale>;
3560
export type UsedLocales = Partial<Record<UsableLocale, LocaleDefinition>>;
3661

37-
export type { LocaleDefinition };
38-
3962
export interface FakerOptions {
4063
locales?: UsedLocales;
4164
locale?: UsableLocale;

src/locales/af_ZA/address/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { AddressDefinitions } from '../../..';
16
import default_country from './default_country';
27
import postcode from './postcode';
38

4-
const address: any = {
9+
const address = {
510
default_country,
611
postcode,
7-
};
12+
} as Partial<AddressDefinitions>;
813

914
export default address;

src/locales/af_ZA/company/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { CompanyDefinitions } from '../../..';
16
import suffix from './suffix';
27

3-
const company: any = {
8+
const company: Partial<CompanyDefinitions> = {
49
suffix,
510
};
611

src/locales/af_ZA/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
15
import type { LocaleDefinition } from '../..';
26
import address from './address';
37
import cell_phone from './cell_phone';
48
import company from './company';
59
import internet from './internet';
6-
import name from './name';
10+
import name_ from './name';
711
import phone_number from './phone_number';
812

913
const af_ZA: LocaleDefinition = {
@@ -12,7 +16,7 @@ const af_ZA: LocaleDefinition = {
1216
cell_phone,
1317
company,
1418
internet,
15-
name,
19+
name: name_,
1620
phone_number,
1721
};
1822

src/locales/af_ZA/internet/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { InternetDefinitions } from '../../..';
16
import domain_suffix from './domain_suffix';
27

3-
const internet: any = {
8+
const internet: Partial<InternetDefinitions> = {
49
domain_suffix,
510
};
611

src/locales/af_ZA/name/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { NameDefinitions } from '../../..';
16
import female_first_name from './female_first_name';
27
import first_name from './first_name';
38
import last_name from './last_name';
49
import male_first_name from './male_first_name';
510

6-
const name: any = {
11+
const name: Partial<NameDefinitions> = {
712
female_first_name,
813
first_name,
914
last_name,

src/locales/af_ZA/phone_number/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { PhoneNumberDefinitions } from '../../..';
16
import formats from './formats';
27

3-
const phone_number: any = {
8+
const phone_number: PhoneNumberDefinitions = {
49
formats,
510
};
611

src/locales/ar/address/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { AddressDefinitions } from '../../..';
16
import building_number from './building_number';
27
import city from './city';
38
import city_name from './city_name';
@@ -11,7 +16,7 @@ import street_address from './street_address';
1116
import street_name from './street_name';
1217
import street_prefix from './street_prefix';
1318

14-
const address: any = {
19+
const address = {
1520
building_number,
1621
city,
1722
city_name,
@@ -24,6 +29,6 @@ const address: any = {
2429
street_address,
2530
street_name,
2631
street_prefix,
27-
};
32+
} as Partial<AddressDefinitions>;
2833

2934
export default address;

src/locales/ar/commerce/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
/*
2+
* This file is automatically generated.
3+
* Run 'pnpm run generate:locales' to update.
4+
*/
5+
import type { CommerceDefinitions } from '../../..';
16
import color from './color';
27
import department from './department';
38
import product_name from './product_name';
49

5-
const commerce: any = {
10+
const commerce: Partial<CommerceDefinitions> = {
611
color,
712
department,
813
product_name,

0 commit comments

Comments
 (0)