Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(generate:locale): make the definition types extendible #915

Merged
merged 4 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 57 additions & 50 deletions scripts/generateLocales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import { resolve } from 'node:path';
import type { Options } from 'prettier';
import { format } from 'prettier';
import options from '../.prettierrc.cjs';
import type { LocaleDefinition } from '../src/definitions';
import { DEFINITIONS } from '../src/definitions';
import type { Definitions, LocaleDefinition } from '../src/definitions';

// Constants

Expand All @@ -33,6 +32,37 @@ const pathDocsApiLocalization = resolve(
'localization.md'
);

// Workaround for nameOf<T>
type PascalCase<S extends string> = S extends `${infer P1}_${infer P2}`
? `${Capitalize<P1>}${PascalCase<P2>}`
: Capitalize<S>;

type DefinitionsType = {
[key in keyof Definitions]: PascalCase<`${key}Definitions`>;
};

/**
* The types of the definitions.
*/
const definitionsTypes: DefinitionsType = {
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
address: 'AddressDefinitions',
animal: 'AnimalDefinitions',
commerce: 'CommerceDefinitions',
company: 'CompanyDefinitions',
database: 'DatabaseDefinitions',
date: 'DateDefinitions',
finance: 'FinanceDefinitions',
hacker: 'HackerDefinitions',
internet: 'InternetDefinitions',
lorem: 'LoremDefinitions',
music: 'MusicDefinitions',
name: 'NameDefinitions',
phone_number: 'PhoneNumberDefinitions',
system: 'SystemDefinitions',
vehicle: 'VehicleDefinitions',
word: 'WordDefinitions',
};

const prettierTsOptions: Options = { ...options, parser: 'typescript' };
const prettierMdOptions: Options = { ...options, parser: 'markdown' };

Expand Down Expand Up @@ -73,13 +103,6 @@ function escapeField(module: string): string {
}
}

function containsAll(checked?: string[], expected?: string[]): boolean {
if (expected == null || checked == null) {
return true;
}
return expected.every((c) => checked.includes(c));
}

function generateLocaleFile(locale: string): void {
let content = `
${autoGeneratedCommentHeader}
Expand Down Expand Up @@ -135,54 +158,47 @@ function generateLocalesIndexFile(
name: string,
type: string,
depth: number,
extra: string = '',
expected?: string[]
extra: string = ''
): void {
let modules = readdirSync(path);
modules = removeIndexTs(modules);
modules = removeTsSuffix(modules);
const importType = type;
if (!containsAll(modules, expected)) {
type = `Partial<${type}>`;
}

const content = [autoGeneratedCommentHeader];
let fieldType = '';
let asType = '';
if (!containsAll(expected, modules)) {
asType = ` as ${type}`;
} else if (type !== 'any') {
fieldType = `: ${type}`;
}
let content = `${autoGeneratedCommentHeader}\n`;
if (type !== 'any') {
content += ` import type { ${importType.replace(
/\[.*/,
''
)} } from '..${'/..'.repeat(depth)}';\n`;
fieldType = `: ${type}`;
content.push(
`import type { ${type.replace(/\[.*/, '')} } from '..${'/..'.repeat(
depth
)}';\n`
);
}
content += ` ${modules
.map((module) => `import ${escapeImport(module)} from './${module}';`)
.join('\n')}
content.push(
...modules.map((m) => `import ${escapeImport(m)} from './${m}';`)
);

const ${name}${fieldType} = {
content.push(`\nconst ${name}${fieldType} = {
${extra}
${modules.map((module) => `${escapeField(module)},`).join('\n')}
}${asType};
};\n`);

export default ${name};
`;
content = format(content, prettierTsOptions);
writeFileSync(resolve(path, 'index.ts'), content);
content.push(`export default ${name};`);

writeFileSync(
resolve(path, 'index.ts'),
format(content.join('\n'), prettierTsOptions)
);
}

function generateRecursiveModuleIndexes(
path: string,
name: string,
definition: string,
depth: number,
extra?: string,
moduleFiles?: string[]
extra?: string
): void {
generateLocalesIndexFile(path, name, definition, depth, extra, moduleFiles);
generateLocalesIndexFile(path, name, definition, depth, extra);

let submodules = readdirSync(path);
submodules = removeIndexTs(submodules);
Expand All @@ -193,18 +209,10 @@ function generateRecursiveModuleIndexes(
if (lstatSync(pathModule).isDirectory()) {
let moduleDefinition =
definition === 'any' ? 'any' : `${definition}['${submodule}']`;
let moduleFiles: string[];

// Overwrite types of src/locales/<locale>/<module>/index.ts for known DEFINITIONS
// Overwrite types of src/locales/<locale>/<module>/index.ts for known definition types
if (depth === 1) {
moduleFiles = DEFINITIONS[submodule];
if (moduleFiles == null) {
moduleDefinition = 'any';
} else {
moduleDefinition = `${submodule.replace(/(^|_)([a-z])/g, (s) =>
s.replace('_', '').toUpperCase()
)}Definitions`;
}
moduleDefinition = definitionsTypes[submodule] ?? 'any';
}

// Recursive
Expand All @@ -213,8 +221,7 @@ function generateRecursiveModuleIndexes(
submodule,
moduleDefinition,
depth + 1,
undefined,
moduleFiles
undefined
);
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/definitions/address.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to addresses.
*/
export interface AddressDefinitions {
export type AddressDefinitions = LocaleEntry<{
/**
* Postcodes patterns by state
*/
Expand All @@ -14,7 +16,7 @@ export interface AddressDefinitions {
/**
* Names of actual cities
*/
city_name?: string[];
city_name: string[];
/**
* Common city prefixes
*/
Expand Down Expand Up @@ -96,4 +98,4 @@ export interface AddressDefinitions {

// A list of timezones names.
time_zone: string[];
}
}>;
6 changes: 4 additions & 2 deletions src/definitions/animal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to animals.
*/
export interface AnimalDefinitions {
export type AnimalDefinitions = LocaleEntry<{
bear: string[];
bird: string[];
cat: string[];
Expand All @@ -16,4 +18,4 @@ export interface AnimalDefinitions {
rabbit: string[];
snake: string[];
type: string[];
}
}>;
6 changes: 4 additions & 2 deletions src/definitions/commerce.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to commerce.
*/
export interface CommerceDefinitions {
export type CommerceDefinitions = LocaleEntry<{
/**
* Human readable color names
*/
Expand All @@ -18,7 +20,7 @@ export interface CommerceDefinitions {
* Descriptions for products.
*/
product_description: string[];
}
}>;

/**
* The possible definitions related to product name generation.
Expand Down
6 changes: 4 additions & 2 deletions src/definitions/company.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to companies.
*/
export interface CompanyDefinitions {
export type CompanyDefinitions = LocaleEntry<{
/**
* Business/products related adjectives.
*/
Expand Down Expand Up @@ -30,4 +32,4 @@ export interface CompanyDefinitions {
* Company suffixes
*/
suffix: string[];
}
}>;
6 changes: 4 additions & 2 deletions src/definitions/database.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to databases.
*/
export interface DatabaseDefinitions {
export type DatabaseDefinitions = LocaleEntry<{
/**
* Database Engine
*/
Expand All @@ -18,4 +20,4 @@ export interface DatabaseDefinitions {
* Column types
*/
type: string[];
}
}>;
6 changes: 4 additions & 2 deletions src/definitions/date.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to dates.
*/
export interface DateDefinitions {
export type DateDefinitions = LocaleEntry<{
/**
* The translations for months (January - December).
*/
Expand All @@ -10,7 +12,7 @@ export interface DateDefinitions {
* The translations for weekdays (Sunday - Saturday).
*/
weekday: DateEntryDefinition;
}
}>;

/**
* The possible definitions related to date entries.
Expand Down
16 changes: 7 additions & 9 deletions src/definitions/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ import type { SystemDefinitions } from './system';
import type { VehicleDefinitions } from './vehicle';
import type { WordDefinitions } from './word';

export type LocaleEntry<T> = Partial<T> &
// Unsupported & custom modules
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Record<string, any>;

/**
* The definitions as used by the Faker modules.
*/
interface Definitions {
export interface Definitions {
address: AddressDefinitions;
animal: AnimalDefinitions;
commerce: CommerceDefinitions;
Expand Down Expand Up @@ -48,11 +53,4 @@ export type LocaleDefinition = {
*/
title: string;
separator?: string;
} & {
// Known modules
[module in keyof Definitions]?: Partial<Definitions[module]>;
} & {
// Unsupported & custom modules
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[group: string]: any;
};
} & LocaleEntry<Definitions>;
6 changes: 4 additions & 2 deletions src/definitions/finance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to finances.
*/
export interface FinanceDefinitions {
export type FinanceDefinitions = LocaleEntry<{
/**
* The types of accounts/purposes of an account (e.g. `Savings` account).
*/
Expand All @@ -21,7 +23,7 @@ export interface FinanceDefinitions {
* Types of transactions (e.g. `deposit`).
*/
transaction_type: string[];
}
}>;

/**
* The possible definitions related to currency entries.
Expand Down
6 changes: 4 additions & 2 deletions src/definitions/hacker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to computers.
*/
export interface HackerDefinitions {
export type HackerDefinitions = LocaleEntry<{
/**
* Generic computer related abbreviations (e.g. `RAM`, `EXE`).
*/
Expand Down Expand Up @@ -30,4 +32,4 @@ export interface HackerDefinitions {
* Some computer related verbs (e.g. `hack`).
*/
verb: string[];
}
}>;
2 changes: 1 addition & 1 deletion src/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type {
export type { CompanyDefinitions } from './company';
export type { DatabaseDefinitions } from './database';
export type { DateDefinitions, DateEntryDefinition } from './date';
export type { LocaleDefinition } from './definitions';
export type { Definitions, LocaleDefinition } from './definitions';
export type {
FinanceCurrencyEntryDefinitions,
FinanceDefinitions,
Expand Down
5 changes: 3 additions & 2 deletions src/definitions/internet.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { EmojiType } from '../modules/internet';
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to internet stuff.
*/
export interface InternetDefinitions {
export type InternetDefinitions = LocaleEntry<{
/**
* Common top level and similar domains (e.g `de`, `co.uk`).
*/
Expand All @@ -20,4 +21,4 @@ export interface InternetDefinitions {
* List of all fully-qualified emojis.
*/
emoji: Record<EmojiType, string[]>;
}
}>;
6 changes: 4 additions & 2 deletions src/definitions/lorem.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { LocaleEntry } from './definitions';

/**
* The possible definitions related to lorem texts.
*/
export interface LoremDefinitions {
export type LoremDefinitions = LocaleEntry<{
/**
* Lorem words used to generate dummy texts.
*/
words: string[];
}
}>;
Loading