diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 2c1b82f4561..664a023e6b2 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -35,15 +35,17 @@ }, { "groupName": "eslint", - "matchPackagePrefixes": [ - "@typescript-eslint/", - "@eslint-types/", - "eslint" + "matchPackageNames": [ + "@eslint-types/**", + "@eslint/**", + "eslint", + "eslint-**", + "typescript-eslint" ] }, { "groupName": "vitest", - "matchPackagePrefixes": ["@vitest/", "vitest"] + "matchPackageNames": ["@vitest/**", "vitest"] }, { "groupName": "prettier", diff --git a/.github/workflows/commentCodeGeneration.ts b/.github/workflows/commentCodeGeneration.ts index 96a520d1020..2e8a115cd18 100644 --- a/.github/workflows/commentCodeGeneration.ts +++ b/.github/workflows/commentCodeGeneration.ts @@ -7,18 +7,34 @@ import type { context as ctx, GitHub } from '@actions/github/lib/utils'; * https://github.com/actions/github-script * * @param github A pre-authenticated octokit/rest.js client with pagination plugins - * @param context An object containing the context of the workflow run - * @param isSuccess A boolean indicating whether the workflow was successful + * @param context An object containing the context of the workflow run. */ export async function script( github: InstanceType, - context: typeof ctx, - isSuccess: boolean + context: typeof ctx ): Promise { + const repoArgs = { owner: context.repo.owner, repo: context.repo.repo }; + + // Identify the PR that triggered the workflow + const head_branch: string = context.payload.workflow_run.head_branch; + const { data: prs } = await github.rest.pulls.list({ + ...repoArgs, + state: 'open', + head: head_branch, + }); + + if (prs.length === 0) { + console.log(`No PRs found for branch ${head_branch}`); + return; + } + + const pr_number = prs[0].number; + + // Check if the PR already has a comment from the bot + const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, + ...repoArgs, + issue_number: pr_number, }); const body = `GitHub Actions has found some problems running the preflight checks. @@ -34,22 +50,24 @@ Please make sure to: (comment) => comment.user?.type === 'Bot' && comment.body?.includes(body) ); - if (isSuccess) { - if (!botComment) return; - await github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - }); - return; - } + const isSuccess = context.payload.workflow_run.conclusion === 'success'; - if (!botComment) { - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body, - }); + if (isSuccess) { + // Delete the bot comment if present + if (botComment != null) { + await github.rest.issues.deleteComment({ + ...repoArgs, + comment_id: botComment.id, + }); + } + } else { + // Create the comment if missing + if (botComment == null) { + await github.rest.issues.createComment({ + ...repoArgs, + issue_number: pr_number, + body, + }); + } } } diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 878e427a970..506f17763bc 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -11,8 +11,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 name: 'Check Code Generation: node-22, ubuntu-latest' - permissions: - pull-requests: write + steps: - name: Checkout uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -52,16 +51,6 @@ jobs: git diff --cached --name-only --exit-code continue-on-error: true - - name: Transpile ts - run: pnpm tsup-node .github/workflows/commentCodeGeneration.ts --format cjs --clean false --out-dir .github/workflows - - - name: Comment - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - const { script } = require('${{ github.workspace }}/.github/workflows/commentCodeGeneration.cjs') - await script(github, context, ${{ steps.generate.outcome == 'success' && steps.diff.outcome == 'success' }}) - - name: Status if: ${{ steps.generate.outcome == 'failure' || steps.diff.outcome == 'failure' }} run: exit 1 diff --git a/.github/workflows/preflight-comment.yml b/.github/workflows/preflight-comment.yml new file mode 100644 index 00000000000..b55d41cf6b1 --- /dev/null +++ b/.github/workflows/preflight-comment.yml @@ -0,0 +1,45 @@ +name: Preflight Comment + +on: + workflow_run: + workflows: 'PR' + types: + - completed + +permissions: + pull-requests: write + +jobs: + comment-code-generation: + runs-on: ubuntu-latest + timeout-minutes: 5 + name: 'Comment Code Generation' + steps: + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + ref: 'next' + + - name: Install pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + + - name: Set node version to 22 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install deps + run: pnpm install + env: + CYPRESS_INSTALL_BINARY: 0 + + - name: Transpile ts + run: pnpm tsup-node .github/workflows/commentCodeGeneration.ts --format cjs --clean false --out-dir .github/workflows + + - name: Comment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const { script } = require('${{ github.workspace }}/.github/workflows/commentCodeGeneration.cjs') + await script(github, context) diff --git a/docs/guide/localization.md b/docs/guide/localization.md index fe7c60a4291..f40a2f48a03 100644 --- a/docs/guide/localization.md +++ b/docs/guide/localization.md @@ -165,3 +165,85 @@ for (let key of Object.keys(allFakers)) { } } ``` + +## Handling Missing Data Errors + +```txt +[Error]: The locale data for 'category.entry' are missing in this locale. +Please contribute the missing data to the project or use a locale/Faker instance that has these data. +For more information see https://fakerjs.dev/guide/localization.html +``` + +If you receive this error, this means you are using a locale (`Faker` instance) that does not have the relevant data for that method yet. +Please consider contributing the missing data, so that others can use them in the future as well. + +As a workaround, you can provide additional fallbacks to your `Faker` instance: + +```ts +import { Faker, el } from '@faker-js/faker'; // [!code --] +import { Faker, el, en } from '@faker-js/faker'; // [!code ++] + +const faker = new Faker({ + locale: [el], // [!code --] + locale: [el, en], // [!code ++] +}); +console.log(faker.location.country()); // 'Belgium' +``` + +::: tip Note +Of course, you can use [Custom Locales and Fallbacks](#custom-locales-and-fallbacks) for this as well. +::: + +## Handling Not-Applicable Data Errors + +```txt +[Error]: The locale data for 'category.entry' aren't applicable to this locale. +If you think this is a bug, please report it at: https://github.com/faker-js/faker +``` + +If you receive this error, this means the current locale is unable to provide reasonable values for that method. +For example, there are no zip codes in Hongkong, so for that reason the `en_HK` locale is unable to provide these data. +The same applies to other locales and methods. + +```ts +import { fakerEN_HK } from '@faker-js/faker'; + +console.log(fakerEN_HK.location.zipCode()); // Error // [!code error] +``` + +For these cases, we explicitly set the data to `null` to clarify, that we have thought about it, but there are no valid values to put there. +We could have used an empty array `[]`, but some locale data are stored as objects `{}`, +so `null` works for both of them without custom downstream handling of missing data. + +::: tip Note +We are by far no experts in all provided languages/countries/locales, +so if you think this is an error for your locale, please create an issue and consider contributing the relevant data. +::: + +If you want to use other fallback data instead, you can define them like this: + +```ts{4} +import { Faker, en, en_HK } from '@faker-js/faker'; + +const faker = new Faker({ + locale: [{ location: { postcode: en.location.postcode } }, en_HK], +}); +console.log(faker.location.zipCode()); // '17551-0348' +``` + +::: warning Warning +Since `null` is considered present data, it will not use any fallbacks for that. +So the following code does **not** work: + +```ts +import { Faker, en, en_HK } from '@faker-js/faker'; + +const faker = new Faker({ + locale: [en_HK, { location: { postcode: en.location.postcode } }], // [!code warning] +}); +console.log(faker.location.zipCode()); // Error // [!code error] +``` + +::: + +See also: [Custom Locales and Fallbacks](#custom-locales-and-fallbacks) diff --git a/docs/guide/upgrading.md b/docs/guide/upgrading.md index 4d9ae22dca4..9eae426956b 100644 --- a/docs/guide/upgrading.md +++ b/docs/guide/upgrading.md @@ -24,12 +24,8 @@ Support for Node.js v14 and v16 has been discontinued as these versions have rea ### Upgrade to TypeScript v5 -We now use TypeScript v5 for our project (previously v4). -While generally not breaking for users, this might have small side effects on our types. - -#### Usage of TypeScript 5 Features - -The helpers module now uses TS5 features, so if you are using Faker with TypeScript, you must use TS5. +Support for TypeScript v4 has been discontinued. Faker v9 requires a minimum of TypeScript v5. +You can see this in action in the helpers module which now uses the [const generic type parameters](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#const-type-parameters) feature. ```ts // v8 @@ -82,15 +78,23 @@ import { } from '@faker-js/faker'; // < v9 default -const f32 = new SimpleFaker({ randomizer: generateMersenne32Randomizer() }); -f32.seed(123); -const r32 = f32.helpers.multiple(() => f32.number.int(10), { count: 10 }); +const oldFaker = new SimpleFaker({ + randomizer: generateMersenne32Randomizer(), +}); +oldFaker.seed(123); +const oldValue = oldFaker.helpers.multiple(() => oldFaker.number.int(10), { + count: 10, +}); // > v9 default -const f53 = new SimpleFaker({ randomizer: generateMersenne53Randomizer() }); -f53.seed(123); -const r53 = f53.helpers.multiple(() => f53.number.int(10), { count: 5 }); +const newFaker = new SimpleFaker({ + randomizer: generateMersenne53Randomizer(), +}); +newFaker.seed(123); +const newValue = newFaker.helpers.multiple(() => newFaker.number.int(10), { + count: 5, +}); -diff(r32, r53); +diff(oldValue, newValue); //[ // 7, // 7, // [!code --] @@ -109,7 +113,9 @@ diff(r32, r53); - If you don't have any seeded tests and just want some random values, then you don't have to change anything. - If you have seeded tests, you have to update most test snapshots or similar comparisons to new values. -- If you are using [vitest](https://vitest.dev), you can do that using `pnpm vitest run -u`. +- For updating snapshots or similar comparisons in different testing frameworks, you can use the following commands: + - **Vitest**: `vitest run --update` + - **Jest**: `jest --updateSnapshot` #### Keeping the Old Behavior @@ -129,9 +135,11 @@ const faker = new Faker({ }); ``` -### Using `tsup` for the Build Process +### Restructured dist folder -After the switch to [tsup](https://tsup.egoist.dev), the `dist` folder now contains minified and chunked files for CJS. However, as we officially support only `exports` defined via `package.json`, this should not affect your code. +The `dist` folder now contains minified and chunked files for CJS, because we switched to [tsup](https://tsup.egoist.dev) for the bundling process. +So it is no longer possible to use `@faker-js/faker/dist/cjs/...`. +However, as we officially support only `exports` defined via `package.json`, this should not affect your code. ## Removals of Deprecated Code @@ -166,7 +174,7 @@ const customEsFakerWithFallback = new Faker({ locale: [es, base] }); Removed deprecated commerce methods -| old | replacement | +| removed | replacement | | --------------------------------------------- | ------------------------------------------------- | | `faker.commerce.price(min, max, dec, symbol)` | `faker.commerce.price({ min, max, dec, symbol })` | @@ -174,7 +182,7 @@ Removed deprecated commerce methods Removed deprecated company methods -| old | replacement | +| removed | replacement | | ----------------------------- | ----------------------------- | | `faker.company.suffixes` | Part of `faker.company.name` | | `faker.company.companySuffix` | Part of `faker.company.name` | @@ -196,13 +204,15 @@ You only need to change your code if you are accessing the raw definitions e.g. | `location.company.prefix` | `location.company.category` | | `location.company.suffix` | `location.direction.legal_entity_type` | -Note: In some locales `prefix`es and `suffix`es might have been swapped, so the mapping might be wrong for those. +::: info Note +In some locales `prefix`es and `suffix`es might have been swapped, so the mapping might be wrong for those. +::: ### Datatype Module Removed deprecated datatype methods -| old | replacement | +| removed | replacement | | --------------------------------------- | ------------------------------------------------------------ | | `faker.datatype.number()` | `faker.number.int()` or `faker.number.float()` | | `faker.datatype.float()` | `faker.number.float()` | @@ -218,7 +228,7 @@ Removed deprecated datatype methods Removed deprecated date methods -| old | replacement | +| removed | replacement | | -------------------------------------- | ------------------------------------------ | | `faker.date.past(years, refDate)` | `faker.date.past({ years, refDate })` | | `faker.date.future(years, refDate)` | `faker.date.future({ years, refDate })` | @@ -233,7 +243,7 @@ Removed deprecated date methods Removed deprecated finance methods -| old | replacement | +| removed | replacement | | --------------------------------------------------------- | ------------------------------------------------------------- | | `faker.finance.account` | `faker.finance.accountNumber` | | `faker.finance.mask` | `faker.finance.maskedNumber` | @@ -244,7 +254,7 @@ Removed deprecated finance methods Removed deprecated git methods -| old | replacement | +| removed | replacement | | ---------------------- | ------------------------------------ | | `faker.git.shortSha()` | `faker.git.commitSha({ length: 7 })` | @@ -252,7 +262,7 @@ Removed deprecated git methods Removed deprecated helpers methods -| old | replacement | +| removed | replacement | | --------------------------------------- | -------------------------------------------------------------- | | `faker.helpers.replaceSymbolWithNumber` | `string.replace(/#+/g, (m) => faker.string.numeric(m.length))` | | `faker.helpers.regexpStyleStringParse` | `faker.helpers.fromRegExp` | @@ -386,7 +396,7 @@ Calling the methods with an empty array instead still behaves as before. Removed deprecated image methods -| old | replacement | +| removed | replacement | | ------------------------- | ------------------------------------------------------------------------------ | | `faker.image.image()` | `faker.image.url()` | | `faker.image.imageUrl()` | `faker.image.url()` | @@ -408,7 +418,7 @@ Removed deprecated image methods Removed deprecated image providers from `faker.image`. They already returned broken image URLs anyway. -| old | replacement | +| removed | replacement | | ------------------------------------------- | -------------------------------------------------------- | | `faker.image.lorempicsum.image` | `faker.image.urlPicsumPhotos` | | `faker.image.lorempicsum.imageGrayscale` | `faker.image.urlPicsumPhotos({ grayscale: true })` | @@ -430,7 +440,7 @@ Removed deprecated image providers from `faker.image`. They already returned bro Removed deprecated internet methods -| old | replacement | +| removed | replacement | | -------------------------------------------------------------- | ----------------------------------------------------------------- | | `faker.internet.avatar()` | `faker.image.avatarLegacy()` or `faker.image.avatar()` | | `faker.internet.email(firstName, lastName, provider, options)` | `faker.internet.email({ firstName, lastName, provider, ... })` | @@ -444,7 +454,7 @@ Removed deprecated internet methods Removed deprecated location methods -| old | replacement | +| removed | replacement | | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | `faker.location.zipCodeByState` | `faker.location.zipCode({ state })` | | `faker.location.cityName` | `faker.location.city` | @@ -478,7 +488,7 @@ The `faker.definitions.location.default_country` definition has been removed, as Removed deprecated number parameter -| old | replacement | +| removed | replacement | | ----------------------------------- | ------------------------------------ | | `faker.number.float({ precision })` | `faker.number.float({ multipleOf })` | @@ -513,15 +523,15 @@ The locale definitions used by `faker.person.jobTitle()`, `faker.person.jobDescr Removed deprecated phone methods -| old | replacement | -| ---------------------------- | -------------------------------------------------------------------------------- | -| `faker.phone.number(format)` | `faker.phone.number()`, `faker.string.numeric()` or `faker.helpers.fromRegExp()` | +| removed | replacement | +| ---------------------------- | ------------------------------------------------------------------------------------- | +| `faker.phone.number(format)` | `faker.phone.number(style)`, `faker.string.numeric()` or `faker.helpers.fromRegExp()` | ### Random Module Removed deprecated random module -| old | replacement | +| removed | replacement | | ----------------------------- | ----------------------------------------------- | | `faker.random.alpha()` | `faker.string.alpha()` | | `faker.random.alphaNumeric()` | `faker.string.alphanumeric()` | @@ -534,7 +544,7 @@ Removed deprecated random module Renamed deprecated locale aliases `cz`, `en_IND`, `ge` and removed `global`. -| old | replacement | +| removed | replacement | | ------------------------------------------------------- | ------------------------------------------------------ | | `import { faker } from '@faker-js/faker/locale/cz'` | `import { faker } from '@faker-js/faker/locale/cs_CZ'` | | `import { faker } from '@faker-js/faker/locale/en_IND'` | `import { faker } from '@faker-js/faker/locale/en_IN'` | @@ -545,7 +555,7 @@ Renamed deprecated locale aliases `cz`, `en_IND`, `ge` and removed `global`. The following locale definitions have been adjusted to align with Faker's locale definition naming standard: -| old | replacement | +| removed | replacement | | ------------------------------------------- | -------------------------------------------- | | `faker.definitions.science.chemicalElement` | `faker.definitions.science.chemical_element` | | `faker.definitions.system.directoryPaths` | `faker.definitions.system.directory_path` | @@ -568,7 +578,7 @@ faker.definitions.science.chemical_element.atomicNumber Removed deprecated type aliases -| old | replacement | +| removed | replacement | | -------------------------------- | ------------------------------- | | `AddressDefinitions` | `LocationDefinition` | | `AirlineDefinitions` | `AirlineDefinition` | @@ -603,7 +613,7 @@ Removed deprecated type aliases Previously, the `faker.date.birthdate()` method had defaults that were unclear in their specific impact. Now, the method requires either none or all of the `min`, `max` and `mode` options. -We also improved the error messages in case of invalid min/max age/year ranges. +We also improved the error messages to clearly indicate when the `min`, `max`, and `mode` options must be set together. ### Fail on Invalid Dates @@ -622,7 +632,7 @@ The `timeZone` functionality has been divided to enhance specificity: - Use `faker.date.timeZone()` to generate a random global time zone. - Use `faker.location.timeZone()` to obtain time zone specific to the current locale. -We haven't updated all locale dependent time zone data yet, so if you encounter unexpected values, please create a new issue. +We haven't updated all locale dependent time zone data yet, so if you encounter unexpected values, please [create a new issue](https://github.com/faker-js/faker/issues/new?template=bug_report.yml). ### Prices Now Return More Price-Like Values @@ -642,13 +652,16 @@ We plan to rethink this method some more in the future: [#2579](https://github.c ### Images Have Random Options by Default -`faker.image.url()` now returns an image url with a random width and height by default. To obtain the previous behavior, pass `{width: 640, height: 480}`. +Some of image methods had static default parameters, previously. +These have been changed to return more divers urls. +Following you can find a table with snippets to obtain the previous behavior: -`faker.image.urlLoremFlickr()` now returns an image url with a random width and height by default. To obtain the previous behavior, pass `{width: 640, height: 480}`. - -`faker.image.urlPicsumPhotos()` now returns an image url with a random width and height by default, additionally images may be converted to grayscale and blurred at random. To obtain the previous behavior, pass `{width: 640, height: 480, blur: 0, grayscale: false}` - -`faker.image.dataUri()` now returns an image url with a random width and height by default, additionally the type of the image is now random. To obtain the previous behavior, pass `{width: 640, height: 480, type: 'svg-uri'}`. +| Method | Old Defaults | +| ------------------------------- | ----------------------------------------------------- | +| `faker.image.url()` | `{width: 640, height: 480}` | +| `faker.image.urlLoremFlickr()` | `{width: 640, height: 480}` | +| `faker.image.urlPicsumPhotos()` | `{width: 640,height: 480, blur: 0, grayscale: false}` | +| `faker.image.dataUri()` | `{width: 640, height: 480, type: 'svg-uri'}` | ### Require `from` and `to` in `faker.date.between` and `betweens` @@ -713,18 +726,3 @@ This affects: - The `format` property of `faker.color.cmyk()`, `faker.color.hsl()`, `faker.color.hwb()`, `faker.color.lab()`, `faker.color.lch()` must be one of `'binary' | 'css' | 'decimal'` if provided - The `variant` property of `faker.location.countryCode()` must be one of `alpha-2`, `alpha-3`, `numeric` if provided - The `casing` property of `faker.string.alpha()` and `faker.string.alphanumeric()` must be one of `'upper' | 'lower' | 'mixed'` if provided - -### Phone Number `style` Replaces Explicit `format` - -`faker.phone.number()` generates a phone number for the current locale. Previously, there was little control over the generated number, which may or may not have included country codes, extensions, white space, and punctuation. - -If you wanted more control over the number, it was previously necessary to pass an explicit `format` parameter. This has now been removed. Instead, you can consider one of two options: - -1. The new `style` parameter has convenient options for common use cases. There are three possible values. - - `'human'`: (default, existing behavior) A human-input phone number, e.g. `555-770-7727` or `555.770.7727 x1234` - - `'national'`: A phone number in a standardized national format, e.g. `(555) 123-4567`. - - `'international'`: A phone number in a E.123 standard international format with country code, e.g. `+15551234567` - -The styles are locale-aware, so for example if you use pt_PT, phone numbers suitable for Portugal would be generated. - -2. If none of the `style`s match your needs, you can use `faker.string.numeric()` or `faker.helpers.fromRegExp()` to create a custom pattern.