aboutsummaryrefslogtreecommitdiff
path: root/src/modules/fake
diff options
context:
space:
mode:
authorShinigami <[email protected]>2022-05-03 15:48:20 +0200
committerGitHub <[email protected]>2022-05-03 15:48:20 +0200
commita2da7c496e9a3741d165ddfe6128b50837fec361 (patch)
tree88d371bc19487bc8a34d9043035aed8e4fedd7d5 /src/modules/fake
parentcc46a0c19af2752b6210c24b715fcce20197b6d9 (diff)
downloadfaker-a2da7c496e9a3741d165ddfe6128b50837fec361.tar.xz
faker-a2da7c496e9a3741d165ddfe6128b50837fec361.zip
refactor!: reorganize src folder (#909)
Diffstat (limited to 'src/modules/fake')
-rw-r--r--src/modules/fake/index.ts134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/modules/fake/index.ts b/src/modules/fake/index.ts
new file mode 100644
index 00000000..a9f2a22a
--- /dev/null
+++ b/src/modules/fake/index.ts
@@ -0,0 +1,134 @@
+import type { Faker } from '../..';
+import { FakerError } from '../../errors/faker-error';
+
+/**
+ * Generator method for combining faker methods based on string input.
+ */
+export class Fake {
+ constructor(private readonly faker: Faker) {
+ // Bind `this` so namespaced is working correctly
+ for (const name of Object.getOwnPropertyNames(Fake.prototype)) {
+ if (name === 'constructor' || typeof this[name] !== 'function') {
+ continue;
+ }
+ this[name] = this[name].bind(this);
+ }
+ }
+
+ /**
+ * Generator for combining faker methods based on a static string input.
+ *
+ * Note: We recommend using string template literals instead of `fake()`,
+ * which are faster and strongly typed (if you are using TypeScript),
+ * e.g. ``const address = `${faker.address.zipCode()} ${faker.address.city()}`;``
+ *
+ * This method is useful if you have to build a random string from a static, non-executable source
+ * (e.g. string coming from a user, stored in a database or a file).
+ *
+ * It checks the given string for placeholders and replaces them by calling faker methods:
+ *
+ * ```js
+ * const hello = faker.fake('Hi, my name is {{name.firstName}} {{name.lastName}}!')
+ * ```
+ *
+ * This would use the `faker.name.firstName()` and `faker.name.lastName()` method to resolve the placeholders respectively.
+ *
+ * It is also possible to provide parameters. At first, they will be parsed as json,
+ * and if that isn't possible, we will fall back to string:
+ *
+ * ```js
+ * const message = faker.fake(`You can call me at {{phone.phoneNumber(+!# !## #### #####!)}}.')
+ * ```
+ *
+ * Currently it is not possible to set more than a single parameter.
+ *
+ * It is also NOT possible to use any non-faker methods or plain javascript in such templates.
+ *
+ * @param str The template string that will get interpolated. Must not be empty.
+ *
+ * @see faker.helpers.mustache() to use custom functions for resolution.
+ *
+ * @example
+ * faker.fake('{{name.lastName}}') // 'Barrows'
+ * faker.fake('{{name.lastName}}, {{name.firstName}} {{name.suffix}}') // 'Durgan, Noe MD'
+ * faker.fake('This is static test.') // 'This is static test.'
+ * faker.fake('Good Morning {{name.firstName}}!') // 'Good Morning Estelle!'
+ * faker.fake('You can call me at {{phone.phoneNumber(!## ### #####!)}}.') // 'You can call me at 202 555 973722.'
+ * faker.fake('I flipped the coin an got: {{helpers.arrayElement(["heads", "tails"])}}') // 'I flipped the coin an got: tails'
+ */
+ fake(str: string): string {
+ // if incoming str parameter is not provided, return error message
+ if (typeof str !== 'string' || str.length === 0) {
+ throw new FakerError('string parameter is required!');
+ }
+
+ // find first matching {{ and }}
+ const start = str.search(/{{[a-z]/);
+ const end = str.indexOf('}}', start);
+
+ // if no {{ and }} is found, we are done
+ if (start === -1 || end === -1) {
+ return str;
+ }
+
+ // extract method name from between the {{ }} that we found
+ // for example: {{name.firstName}}
+ const token = str.substring(start + 2, end + 2);
+ let method = token.replace('}}', '').replace('{{', '');
+
+ // extract method parameters
+ const regExp = /\(([^)]+)\)/;
+ const matches = regExp.exec(method);
+ let parameters = '';
+ if (matches) {
+ method = method.replace(regExp, '');
+ parameters = matches[1];
+ }
+
+ // split the method into module and function
+ const parts = method.split('.');
+
+ if (this.faker[parts[0]] == null) {
+ throw new FakerError(`Invalid module: ${parts[0]}`);
+ }
+
+ if (this.faker[parts[0]][parts[1]] == null) {
+ throw new FakerError(`Invalid method: ${parts[0]}.${parts[1]}`);
+ }
+
+ // assign the function from the module.function namespace
+ let fn: (args?: unknown) => string = this.faker[parts[0]][parts[1]];
+ fn = fn.bind(this);
+
+ // If parameters are populated here, they are always going to be of string type
+ // since we might actually be dealing with an object or array,
+ // we always attempt to the parse the incoming parameters into JSON
+ let params: unknown;
+ // Note: we experience a small performance hit here due to JSON.parse try / catch
+ // If anyone actually needs to optimize this specific code path, please open a support issue on github
+ try {
+ params = JSON.parse(parameters);
+ } catch (err) {
+ // since JSON.parse threw an error, assume parameters was actually a string
+ params = parameters;
+ }
+
+ let result: string;
+ if (typeof params === 'string' && params.length === 0) {
+ result = String(fn());
+ } else {
+ result = String(fn(params));
+ }
+
+ // Replace the found tag with the returned fake value
+ // We cannot use string.replace here because the result might contain evaluated characters
+ const res = str.substring(0, start) + result + str.substring(end + 2);
+
+ if (res === '') {
+ return '';
+ }
+
+ // return the response recursively until we are done finding all tags
+ return this.fake(res);
+ }
+}