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

Open Faker for custom/community modules #704

Open
xDivisionByZerox opened this issue Mar 28, 2022 · 11 comments
Open

Open Faker for custom/community modules #704

xDivisionByZerox opened this issue Mar 28, 2022 · 11 comments
Labels
c: feature Request for new feature has workaround Workaround provided or linked p: 1-normal Nothing urgent s: waiting for user interest Waiting for more users interested in this feature
Milestone

Comments

@xDivisionByZerox
Copy link
Member

xDivisionByZerox commented Mar 28, 2022

Clear and concise description of the problem

Faker has many different modules but even more requested features.
Many of these features are sadly too complex to be maintained by the Faker-JS team.

Suggested solution

The Faker class should accept a list of modules that will be used under the created Faker instance. These modules will be plugins maintained by the community.
Like:

const myModule = {
    test() {
        return 'test';
    }
}

const fakerWithCustomModule = new Faker({
    customModules: [
        { name: 'testModule', module: myModule },
    ],
});
fakerWithCustomModule.testModule.test(); // 'test'

Alternative

No response

Additional context

Somewhat related to #444

@xDivisionByZerox xDivisionByZerox added c: feature Request for new feature p: 1-normal Nothing urgent s: needs decision Needs team/maintainer decision labels Mar 28, 2022
@xDivisionByZerox xDivisionByZerox added this to the Future milestone Mar 28, 2022
@ST-DDT
Copy link
Member

ST-DDT commented Mar 28, 2022

Here is a workaround to do this right now in a typesafe way:

import type { Faker, UsableLocale } from "@faker-js/faker";
import { faker } from "@faker-js/faker";

function withCustomModule<
  S extends Faker,
  Name extends string,
  T,
  U extends S & Record<Name, T>
>(base: S, name: Name, value: T): U {
  return {
    // Class level functions
    seed: (seed: number | number[]): void => base.seed(seed),
    setLocale: (locale: UsableLocale): void => base.setLocale(locale),
    // Previous modules
    ...base,
    // Custom Modules
    [name]: value,
  } as U;
}

const customModule = {
  abc: () => "sjkdfhajkshdf",
} as const;

const fakerPlus = withCustomModule(faker, "test", customModule);

console.log(fakerPlus.test.abc());

@ST-DDT ST-DDT added the has workaround Workaround provided or linked label Mar 28, 2022
@ST-DDT ST-DDT moved this to Todo in Faker Roadmap Mar 28, 2022
@xDivisionByZerox
Copy link
Member Author

@ST-DDT
Copy link
Member

ST-DDT commented Apr 26, 2022

Isn't that almost like my example above?
(Just with new/a constructor and Object.assign instead of a Record)

@xDivisionByZerox
Copy link
Member Author

Yes, I just wanted to provide an example of how other communities handle the extendability approach.

@ST-DDT ST-DDT added s: needs decision Needs team/maintainer decision s: waiting for user interest Waiting for more users interested in this feature and removed s: needs decision Needs team/maintainer decision labels Mar 16, 2023
@ST-DDT
Copy link
Member

ST-DDT commented Mar 16, 2023

If you are interested in this feature, please upvote it.

@ST-DDT ST-DDT removed the s: needs decision Needs team/maintainer decision label Mar 16, 2023
@ST-DDT ST-DDT added s: waiting for user interest Waiting for more users interested in this feature and removed s: waiting for user interest Waiting for more users interested in this feature labels May 5, 2023
@github-actions
Copy link
Contributor

github-actions bot commented May 5, 2023

Thank you for your feature proposal.

We marked it as "waiting for user interest" for now to gather some feedback from our community:

  • If you would like to see this feature be implemented, please react to the description with an up-vote (:+1:).
  • If you have a suggestion or want to point out some special cases that need to be considered, please leave a comment, so we are aware about them.

We would also like to hear about other community members' use cases for the feature to give us a better understanding of their potential implicit or explicit requirements.

We will start the implementation based on:

  • the number of votes ( 👍 ) and comments
  • the relevance for the ecosystem
  • availability of alternatives and workarounds
  • and the complexity of the requested feature

We do this because:

  • There are plenty of languages/countries out there and we would like to ensure that every method can cover all or almost all of them.
  • Every feature we add to faker has "costs" associated to it:
    • initial costs: design, implementation, reviews, documentation
    • running costs: awareness of the feature itself, more complex module structure, increased bundle size, more work during refactors

View more issues which are waiting for user interest

@ST-DDT
Copy link
Member

ST-DDT commented May 8, 2024

We might need something like this for fake with the standalone module functions.

I think it might end up in something like a registry:

registry.module.method

@matthewmayer
Copy link
Contributor

I appreciate this issue has a lot of upvotes, but I feel I'm maybe missing something about the potential use-cases.

For example, suppose you really want faker to support random tree names but you notice #2022 doesn't have enough up-votes yet.

You can already easily create your own function using your own data with faker helper methods e.g. return faker.helpers.arrayElement(["ash","oak","pine","palm" ... ])

What is gained by making this one-line function into a "community module"? By using faker.helpers functions you already can take advantage of reproducible seeds etc. And core modules wouldn't be able to refer to community modules in their definitions. If you just want to share the data, you could already publish an NPM package which is just module.exports = ["ash","oak","pine","palm" ... ] and feed that into faker.helpers.arrayElement.

@ST-DDT
Copy link
Member

ST-DDT commented May 13, 2024

AFAICT: The are currently 3 things this is intended for:

  • make it callable via faker.moduleA.functionB
  • make it able to use faker.definitions.categoryC.entryD and throw a useful error if it is missing
  • make it usable via faker.helpers.fake

But also as a reminder, that whatever we are going to do for #2667 + #2664 it should be possible for our community to do the same for their functions/modules.

What I hope to do with that:

  • Overwrite faker.string.fromRegExp with a version with dependencies supporting "full" regex (as plugin/extension).

@matthewmayer
Copy link
Contributor

But what is the advantage of having it callable via faker.moduleA.functionB rather than just a seperate module/function? It sees it makes it unclear what is a core Faker function and what is an extension/community addition? You could also have clashes if say someone creates a "nature" module and then later we add "nature" to core.

@ST-DDT
Copy link
Member

ST-DDT commented May 13, 2024

But what is the advantage of having it callable via faker.moduleA.functionB rather than just a seperate module/function?

None, other than consistency with the other calls. It has been requested in #444.
How we will eventually achieve that, I don't want to make any predictions yet ("sub-classing", composition, ...).

It sees it makes it unclear what is a core Faker function and what is an extension/community addition?

True, and we have to look into that. Would you consider that an important distinction and why (not)?

You could also have clashes if say someone creates a "nature" module and then later we add "nature" to core.

The extension would win over the provided methods and thus nothing changes for the user, except that they might get additional suggestions for the native methods. In the end, it is just an outdated/obsolete dependency in your project, that you didn't remove because you didn't read the changelog/release announcement.
My hope/assumption is that all features that are eligible for inclusion will be added to Faker itself before the need arises to create an extension for that (aka the user interest required for a plugin to be published ~= the user interest required for inclusion in faker).

As a side note:

  1. make it callable via faker.moduleA.functionB
  2. make it able to use faker.definitions.categoryC.entryD and throw a useful error if it is missing
  3. make it usable via faker.helpers.fake

1 is already possible, although only with some JS/TS tricks, that I don't want to endorse at this point.
2 is already usable.
And 3 might come for free depending on how we implement the standalone module functions (aka do we have a registry of some kind or not), well you could even achieve 1 depending on how you do/use the registry it.

Speculation: Maybe we replace faker.helpers.fake with localized function invocations ([(fakerCore) -> `Welcome ${firstName(fakerCore)} to your exciting adventure`]) and thus we don't need the registry to allow for better tree-shaking.
We have to keep this issue in mind when doing the v9.0 changes, but the extension system is not a v9.0 goal IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: feature Request for new feature has workaround Workaround provided or linked p: 1-normal Nothing urgent s: waiting for user interest Waiting for more users interested in this feature
Projects
No open projects
Status: Todo
Development

No branches or pull requests

3 participants