Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
30e56f9
feat: introduce fakerRegistry
ST-DDT Apr 19, 2026
e6a75d2
docs: prepare for SMFs
ST-DDT Apr 19, 2026
0656236
refactor: transform basics
ST-DDT Apr 9, 2026
65e54b5
test: enable SMF refresh test
ST-DDT Apr 19, 2026
d1b7121
Merge branch 'next' into feat/samfn/utils
ST-DDT Apr 22, 2026
4a50173
chore: replace SMF term
ST-DDT Apr 23, 2026
c06e403
chore: explicit registry typing
ST-DDT Apr 23, 2026
df682b8
docs: fix apidocs refresh
ST-DDT Apr 23, 2026
48f88f6
Update src/registry.types.ts
ST-DDT Apr 23, 2026
b844881
chore: edit after review
ST-DDT Apr 24, 2026
5100ade
Merge branch 'next' into feat/samfn/utils
ST-DDT Apr 24, 2026
36d13af
chore: hide default value
ST-DDT Apr 25, 2026
dd4090e
chore: use code groups for examples
ST-DDT Apr 25, 2026
f25f8d7
chore: simplify code
ST-DDT Apr 25, 2026
a739b8f
chore: fix examples
ST-DDT Apr 25, 2026
8bc25a8
Update test/scripts/apidocs/verify-jsdoc-tags.spec.ts
ST-DDT Apr 25, 2026
aae7f8b
Update test/scripts/apidocs/verify-jsdoc-tags.spec.ts
ST-DDT Apr 25, 2026
87eab2a
Merge branch 'next' into feat/samfn/utils
ST-DDT Apr 26, 2026
f9b1812
Merge branch 'next' into feat/samfn/utils
ST-DDT May 1, 2026
4dfd622
Merge branch 'next' into feat/samfn/utils
ST-DDT May 3, 2026
14f8028
Merge branch 'next' into feat/samfn/utils
ST-DDT May 6, 2026
b8a1cd0
chore: rename for consistency
ST-DDT May 8, 2026
b328002
Merge branch 'next' into feat/samfn/utils
ST-DDT May 10, 2026
60eb425
refactor: remove registry
ST-DDT May 10, 2026
1079527
chore: cleanup
ST-DDT May 10, 2026
563e71f
chore: update snapshots
ST-DDT May 10, 2026
11ffcf4
chore: extend tests
ST-DDT May 10, 2026
e25a39d
chore: cleanup
ST-DDT May 10, 2026
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
2 changes: 1 addition & 1 deletion docs/.vitepress/components/api-docs/refreshable-code.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function initRefresh(): Element[] {
// Keep in sync with ref scripts/shared/refreshable-code.ts
if (
domLines[lineIndex]?.children.length === 0 ||
!/^\w*faker\w*\.|^distributor\(/i.test(
!/^\w*faker\w*\.|^\w+\(fakerCore|^distributor\(/i.test(
domLines[lineIndex]?.textContent ?? ''
)
) {
Expand Down
41 changes: 34 additions & 7 deletions scripts/apidocs/output/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,30 @@ editLink: false
* @param pages The pages to write.
*/
export async function writePages(pages: RawApiDocsPage[]): Promise<void> {
await Promise.all(pages.map(writePage));
const registryHints: Record<string, string> = Object.fromEntries(
pages.flatMap((page) =>
page.methods.map((method) => [
method.name,
`${page.camelTitle}.${method.name}`,
])
)
);
Comment thread
xDivisionByZerox marked this conversation as resolved.
await Promise.all(pages.map((page) => writePage(page, registryHints)));
}

/**
* Writes the api docs page and data for the given module to the correct location.
*
* @param page The page to write.
* @param registryHints Hints for accessing standalone functions via module registry.
*/
async function writePage(page: RawApiDocsPage): Promise<void> {
async function writePage(
page: RawApiDocsPage,
registryHints: Record<string, string> = {}
): Promise<void> {
try {
await writePageMarkdown(page);
await writePageData(page);
await writePageData(page, registryHints);
} catch (error) {
throw new Error(`Error writing page ${page.title}`, { cause: error });
}
Expand Down Expand Up @@ -103,20 +115,34 @@ async function writePageMarkdown(page: RawApiDocsPage): Promise<void> {
* Writes the api docs data for the given module to correct location.
*
* @param page The page to write.
* @param registryHints Hints for accessing standalone functions via module registry.
*/
async function writePageData(page: RawApiDocsPage): Promise<void> {
async function writePageData(
page: RawApiDocsPage,
registryHints: Record<string, string> = {}
): Promise<void> {
const { camelTitle, methods } = page;
const pageData: Record<string, ApiDocsMethod> = Object.fromEntries(
await Promise.all(
methods.map(async (method) => [method.name, await toMethodData(method)])
)
);
const prioritizedRegistryHints = {
...registryHints,
// own module > other modules
...Object.fromEntries(
methods.map((method) => [method.name, `${camelTitle}.${method.name}`])
),
// utils always win
getDefaultRefDate: 'defaultRefDate',
setDefaultRefDate: 'setDefaultRefDate',
};

const refreshFunctions: Record<string, string> = Object.fromEntries(
await Promise.all(
methods.map(async (method) => [
method.name,
await toRefreshFunction(method),
await toRefreshFunction(method, prioritizedRegistryHints),
])
)
);
Expand Down Expand Up @@ -218,12 +244,13 @@ export function extractSummaryDefault(description: string): string | undefined {
}

export async function toRefreshFunction(
method: RawApiDocsMethod
method: RawApiDocsMethod,
registryHints: Record<string, string> = {}
): Promise<string> {
const { name, signatures } = method;
const signatureData = required(signatures.at(-1), 'method signature');
const { examples } = signatureData;

const exampleCode = examples.join('\n');
return await toRefreshableCode(name, exampleCode);
return await toRefreshableCode(name, exampleCode, registryHints);
}
28 changes: 24 additions & 4 deletions scripts/shared/refreshable-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,33 @@ import { formatTypescript } from '../shared/format';

export async function toRefreshableCode(
name: string,
exampleCode: string
exampleCode: string,
moduleHints: Record<string, string> = {}
): Promise<string> {
const exampleLines = exampleCode
.replaceAll(/ ?\/\/.*$/gm, '') // Remove comments
.replaceAll(/^import .*$/gm, '') // Remove imports
.replaceAll(
// record results of relevant calls
// Keep in sync with docs/.vitepress/components/api-docs/refreshable-code.vue
/^(\w*faker\w*\..+(?:(?:.|\n..)*\n[^ ])?\)(?:\.\w+)?|distributor\(.+\));?$/gim,
`try { result.push($1); } catch (error: unknown) { result.push(error instanceof Error ? error.name : 'Error'); }\n`
/\b(?<!\.)(\w+)\((faker\.)?fakerCore,?/g,
(_, methodName) => {
// We only have access to the main index imports, so we call the functions on the main faker object instead.
// e.g.: firstName(fakerCore) -> faker.person.firstName()
const hint = moduleHints[methodName];
if (hint != null) {
return `faker.${hint}(`;
}

throw new Error(
`Unable to find module hint for ${methodName} in example code for ${name}`
);
}
)
.replaceAll(
// Record results of relevant calls
// Keep in sync with docs/.vitepress/components/api-docs/refreshable-code.vue
/^((?<callBase>\w*faker\w*\.|distributor\()(?<consumeToEOL>.+)(?<multiline>(?<consumeIndented>\n +.*)+(?<finalLine>\n[^ \n]+))?\)(?<nestedProperty>\.\w+)?);?$/gim,
`try { result.push($1); } catch (error: unknown) { result.push(error instanceof Error ? error.name : 'Error'); console.log('Error in example for ${name}:', error); }\n`
);

if (!exampleLines.includes('try { result.push(')) {
Expand All @@ -22,9 +39,12 @@ export async function toRefreshableCode(
const fullMethod = `async (): Promise<unknown[]> => {
await enableFaker();
const result: unknown[] = [];
${/(?<!\.)fakerCore/.test(exampleCode) ? 'const { fakerCore } = faker;' : ''}

${exampleLines}

${exampleCode.includes('setDefaultRefDate(') ? 'faker.setDefaultRefDate(); // Reset' : ''}

return result;
}`;
try {
Expand Down
3 changes: 3 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export interface FakerOptions {
/**
* Helper function to create a FakerCore instance.
*
* @remark The `config`, `randomizer` and (single) `locale` instances can be shared between multiple cores.
* When shared, changing them will affect the other cores as well.
*
* @param options The options to create the FakerCore instance with.
* @param options.locale The locale definitions to use.
* If not provided, this core will not have any locale data and thus all methods that rely on locale data will throw an error when called.
Expand Down
26 changes: 11 additions & 15 deletions src/simple-faker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { SimpleHelpersModule } from './modules/helpers';
import { SimpleLocationModule } from './modules/location';
import { NumberModule } from './modules/number';
import { StringModule } from './modules/string';

export const DEFAULT_REF_DATE_SOURCE: () => Date = () => new Date();
import { getDefaultRefDate as utilsGetDefaultRefDate } from './utils/get-default-ref-date';
import { setDefaultRefDate as utilsSetDefaultRefDate } from './utils/set-default-ref-date';

/**
* This is a simplified Faker class that doesn't need any localized data to generate its output.
Expand Down Expand Up @@ -42,7 +42,7 @@ export class SimpleFaker {
* Gets a new reference date used to generate relative dates.
*/
get defaultRefDate(): () => Date {
return this.fakerCore.config.defaultRefDate ?? DEFAULT_REF_DATE_SOURCE;
return () => utilsGetDefaultRefDate(this.fakerCore);
}

/**
Expand All @@ -56,17 +56,17 @@ export class SimpleFaker {
* @see faker.seed(): For generating reproducible values.
*
* @example
* faker.seed(1234);
*
* // Default behavior
* // Default
* faker.seed(1234); // Keep `past()` offset consistent for example runs
* // faker.setDefaultRefDate();
* faker.date.past(); // Changes based on the current date/time
*
* // Use a static ref date
* @example
* // Fixed
* faker.seed(1234);
* faker.setDefaultRefDate(new Date('2020-01-01'));
* faker.date.past(); // Reproducible '2019-07-03T08:27:58.118Z'
*
* // Use a ref date that changes every time it is used
* @example
* // Tick on use
* let clock = new Date("2020-01-01").getTime();
* faker.setDefaultRefDate(() => {
* clock += 1000; // +1s
Expand All @@ -81,11 +81,7 @@ export class SimpleFaker {
setDefaultRefDate(
dateOrSource: string | Date | number | (() => Date) = () => new Date()
): void {
if (typeof dateOrSource === 'function') {
this.fakerCore.config.defaultRefDate = dateOrSource;
} else {
this.fakerCore.config.defaultRefDate = () => new Date(dateOrSource);
}
utilsSetDefaultRefDate(this.fakerCore, dateOrSource);
}

readonly datatype: DatatypeModule = new DatatypeModule(this);
Expand Down
39 changes: 39 additions & 0 deletions src/utils/get-default-ref-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { FakerCore } from '../core';

const DEFAULT_REF_DATE_SOURCE: () => Date = () => new Date();

/**
* Gets a new reference date used to generate relative dates.
*
* If `fakerCore.config.defaultRefDate` is defined, it will be used to get the default reference date. Otherwise, the current date will be used.
*
* @param fakerCore The FakerCore instance to get it from.
*
* @returns The newly created default reference date.
*
* @example
* // Default
* fakerCore.randomizer.seed(1234); // Keep `past()` offset consistent for example runs
* // setDefaultRefDate(fakerCore);
* past(fakerCore); // Changes based on the current date/time
* @example
* // Fixed
* fakerCore.randomizer.seed(1234);
* setDefaultRefDate(fakerCore, new Date('2020-01-01'));
* past(fakerCore); // Reproducible '2019-07-03T08:27:58.118Z'
* @example
* // Tick on use
* let clock = new Date("2020-01-01").getTime();
* setDefaultRefDate(fakerCore, () => {
* clock += 1000; // +1s
* return new Date(clock);
* });
*
* getDefaultRefDate(fakerCore) // 2020-01-01T00:00:01Z
* getDefaultRefDate(fakerCore) // 2020-01-01T00:00:02Z
Comment thread
ST-DDT marked this conversation as resolved.
*
* @since 10.5.0
*/
export function getDefaultRefDate(fakerCore: FakerCore): Date {
return (fakerCore.config.defaultRefDate ?? DEFAULT_REF_DATE_SOURCE)();
}
Comment thread
xDivisionByZerox marked this conversation as resolved.
46 changes: 46 additions & 0 deletions src/utils/set-default-ref-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { FakerCore } from '../core';

/**
* Sets the `refDate` source to use if no `refDate` date is passed to the date methods.
*
* @param fakerCore The FakerCore instance to use.
* @param dateOrSource The function or the static value used to generate the `refDate` date instance.
* The function must return a new valid `Date` instance for every call.
* Defaults to `() => new Date()`.
*
* @see [Reproducible Results](https://fakerjs.dev/guide/usage.html#reproducible-results)
* @see faker.seed(): For generating reproducible values.
*
* @example
* // Default
* fakerCore.randomizer.seed(1234); // Keep `past()` offset consistent for example runs
* // setDefaultRefDate(fakerCore);
* past(fakerCore); // Changes based on the current date/time
* @example
* // Fixed
* fakerCore.randomizer.seed(1234);
* setDefaultRefDate(fakerCore, new Date('2020-01-01'));
* past(fakerCore); // Reproducible '2019-07-03T08:27:58.118Z'
Comment thread
ST-DDT marked this conversation as resolved.
* @example
* // Tick on use
* let clock = new Date("2020-01-01").getTime();
* setDefaultRefDate(fakerCore, () => {
* clock += 1000; // +1s
* return new Date(clock);
* });
*
* getDefaultRefDate(fakerCore) // 2020-01-01T00:00:01Z
* getDefaultRefDate(fakerCore) // 2020-01-01T00:00:02Z
*
* @since 10.5.0
*/
export function setDefaultRefDate(
fakerCore: FakerCore,
dateOrSource: string | Date | number | (() => Date) = () => new Date()
): void {
if (typeof dateOrSource === 'function') {
fakerCore.config.defaultRefDate = dateOrSource;
} else {
fakerCore.config.defaultRefDate = () => new Date(dateOrSource);
}
}
30 changes: 30 additions & 0 deletions test/scripts/apidocs/__snapshots__/page.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exports[`toRefreshFunction > should handle multiline calls 1`] = `
);
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
Expand All @@ -29,12 +30,14 @@ exports[`toRefreshFunction > should handle multiple calls 1`] = `
result.push(faker.number.int());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

try {
result.push(faker.number.int());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
Expand All @@ -50,6 +53,7 @@ exports[`toRefreshFunction > should handle properties after calls 1`] = `
result.push(faker.airline.airport().name);
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
Expand All @@ -65,6 +69,7 @@ exports[`toRefreshFunction > should handle single line calls with semicolon 1`]
result.push(faker.number.int());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
Expand All @@ -80,6 +85,31 @@ exports[`toRefreshFunction > should handle single line calls without semicolon 1
result.push(faker.number.int());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
}"
`;

exports[`toRefreshFunction > should handle standalone functions calls 1`] = `
"async (): Promise<unknown[]> => {
await enableFaker();
const result: unknown[] = [];
const { fakerCore } = faker;

try {
result.push(faker.defaultRefDate());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

try {
result.push(faker.date.past());
} catch (error: unknown) {
result.push(error instanceof Error ? error.name : 'Error');
console.log('Error in example for test:', error);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ exports[`check docs completeness > all modules and methods are present 1`] = `
[
"generateMersenne32Randomizer",
"generateMersenne53Randomizer",
"getDefaultRefDate",
"mergeLocales",
"setDefaultRefDate",
],
],
[
Expand Down
Loading
Loading