diff --git a/.changeset/add_new_option_javascriptjsxeverywhere.md b/.changeset/add_new_option_javascriptjsxeverywhere.md new file mode 100644 index 000000000000..dd4844604b6f --- /dev/null +++ b/.changeset/add_new_option_javascriptjsxeverywhere.md @@ -0,0 +1,9 @@ +--- +"@biomejs/biome": minor +--- + +Add new option `javascript.parser.jsxEverywhere`. This new option allows to control whether Biome should expect JSX syntax in `.js`/`.ts` files. + +When `jsxEverywhere` is set to `false`, having JSX syntax like `
` inside `.js`/`.ts` files will result in a **parsing error**. + +This option defaults to `true`. diff --git a/.changeset/add_the_new_rule_nofloatingpromises.md b/.changeset/add_the_new_rule_nofloatingpromises.md new file mode 100644 index 000000000000..3a80e6da2c43 --- /dev/null +++ b/.changeset/add_the_new_rule_nofloatingpromises.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Add the new rule [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises). diff --git a/.changeset/add_the_new_rule_noimportcycles.md b/.changeset/add_the_new_rule_noimportcycles.md new file mode 100644 index 000000000000..b0296c588606 --- /dev/null +++ b/.changeset/add_the_new_rule_noimportcycles.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Add the new rule [`noImportCycles`](https://biomejs.dev/linter/rules/no-import-cycles). diff --git a/.changeset/add_the_new_rule_notsignorehttps.md b/.changeset/add_the_new_rule_notsignorehttps.md new file mode 100644 index 000000000000..6999a8a84732 --- /dev/null +++ b/.changeset/add_the_new_rule_notsignorehttps.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Add the new rule [`noTsIgnore`](https://biomejs.dev/linter/rules/no-ts-ignore). diff --git a/.changeset/add_the_new_rule_nounwantedpolyfillio.md b/.changeset/add_the_new_rule_nounwantedpolyfillio.md new file mode 100644 index 000000000000..e8fe8d8c1ce9 --- /dev/null +++ b/.changeset/add_the_new_rule_nounwantedpolyfillio.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Add the new rule [`noUnwantedPolyfillio`](https://biomejs.dev/linter/rules/no-unwanted-polyfillio). diff --git a/.changeset/add_whitespace_after_css_selecters_preceded_by_comment.md b/.changeset/add_whitespace_after_css_selecters_preceded_by_comment.md new file mode 100644 index 000000000000..ff9ad381b107 --- /dev/null +++ b/.changeset/add_whitespace_after_css_selecters_preceded_by_comment.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#5001](https://github.com/biomejs/biome/issues/5001), where the CSS formatter removes whitespace from selector preceded by a comment diff --git a/.changeset/added_a_json_format_option_expandslists.md b/.changeset/added_a_json_format_option_expandslists.md new file mode 100644 index 000000000000..67f1081bbaf5 --- /dev/null +++ b/.changeset/added_a_json_format_option_expandslists.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Added a JSON format option `expand`. The option `json.formatter.expand` allows to enforce the formatting of arrays and objects on multiple lines, regardless of their length. diff --git a/.changeset/better_control_over_linter_groups.md b/.changeset/better_control_over_linter_groups.md new file mode 100644 index 000000000000..367420e91364 --- /dev/null +++ b/.changeset/better_control_over_linter_groups.md @@ -0,0 +1,30 @@ +--- +"@biomejs/biome": minor +--- + +Linter groups now accept new options to enable/disable all rules that belong to a group, and control the severity +of the rules that belong to those groups. + +For example, you can downgrade the severity of rules that belong to `"style"` to emit `"info"` diagnostics: + +```json +{ + "linter": { + "rules": { + "style": "info" + } + } +} +``` + +You can also enable all rules that belong to a group using the default severity of the rule using the `"on"` option: + +```json +{ + "linter": { + "rules": { + "complexity": "on" + } + } +} +``` diff --git a/.changeset/biome_assist.md b/.changeset/biome_assist.md new file mode 100644 index 000000000000..97cd20cd5b71 --- /dev/null +++ b/.changeset/biome_assist.md @@ -0,0 +1,53 @@ +--- +"@biomejs/biome": minor +--- + +Biome assist is a new feature of the Biome analyzer. The assist is meant to provide **actions**. Actions differ from linter rules in that they aren't meant to signal errors. + +The assist will provide code actions that users can opt into via configuration or via IDEs/editors, using the Language Server Protocol. + +The assist **is enabled by default**. However, you can turn if off via configuration: + +```json +{ + "assist": { + "enabled": false + } +} +``` + +You can turn on the actions that you want to use in your configuration. For example, you can enable the `useSortedKeys` action like this: + +```json +{ + "assist": { + "actions": { + "source": { + "useSortedKeys": "on" + } + } + } +} +``` + +Alternatively, IDE/editor users can decide which action to apply on save *directly from the editor settings*, as long as the assist is enabled. + +For example, in VS Code you can apply the `useSortedKeys` action when saving a file by adding the following snippet in `settings.json`: + +```json +{ + "editor.codeActionsOnSave": { + "source.biome.useSortedKeys": "explicit" + } +} +``` + +In Zed, you can achieve the same by adding the following snippet in `~/.config/zed/settings.json`: + +```json +{ + "code_actions_on_format": { + "source.biome.useSortedKeys": true + } +} +``` diff --git a/.changeset/biome_logs_a_warning_in_case_a_folder_contains_biomejson_and_biomejsonc_and_it_will_use_biomejson_by_default.md b/.changeset/biome_logs_a_warning_in_case_a_folder_contains_biomejson_and_biomejsonc_and_it_will_use_biomejson_by_default.md new file mode 100644 index 000000000000..ac4d506100b2 --- /dev/null +++ b/.changeset/biome_logs_a_warning_in_case_a_folder_contains_biomejson_and_biomejsonc_and_it_will_use_biomejson_by_default.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Biome logs a warning in case a folder contains `biome.json` and `biome.jsonc`, and it will use `biome.json` by default. diff --git a/.changeset/biome_migrate_eslint_rule_overriding.md b/.changeset/biome_migrate_eslint_rule_overriding.md new file mode 100644 index 000000000000..91fc475b789f --- /dev/null +++ b/.changeset/biome_migrate_eslint_rule_overriding.md @@ -0,0 +1,38 @@ +--- +"@biomejs/biome": minor +--- + +Biome migrate eslint outputs a better overriding behavior. + +A Biome rule can have multiple ESLint equivalent rules. +For example, [useLiteralKeys](https://biomejs.dev/linter/rules/use-literal-keys/) has two ESLint equivalent rules: [dot-notation](https://eslint.org/docs/latest/rules/dot-notation) and [@typescript-eslint/dot-notation](https://typescript-eslint.io/rules/dot-notation/). + +Previously, Biome wouldn't always enable a Biome rule even if one of its equivalent rules was enabled. +Now Biome uses the higher severity level of all the equivalent ESLint rules to set the severity level of the Biome rule. + +The following ESLint configuration... + +```json +{ + "rules": { + "@typescript-eslint/dot-notation": "error", + "dot-notation": "off" + } +} +``` + +...is now migrated to... + +```json +{ + "linter": { + "rules": { + "complexity": { + "useLiteralKeys": "error" + } + } + } +} +``` + +...because `error` is higher than `off`. diff --git a/.changeset/biome_now_resolves_globs_and_paths_from_the_configuration_before_paths_and_globs_were_resolved_from_the_working_directory.md b/.changeset/biome_now_resolves_globs_and_paths_from_the_configuration_before_paths_and_globs_were_resolved_from_the_working_directory.md new file mode 100644 index 000000000000..1e653d38b4a8 --- /dev/null +++ b/.changeset/biome_now_resolves_globs_and_paths_from_the_configuration_before_paths_and_globs_were_resolved_from_the_working_directory.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +Biome now resolves globs and paths from the configuration. Before, paths and globs were resolved from the working directory. diff --git a/.changeset/blazing-fires-burn.md b/.changeset/blazing-fires-burn.md new file mode 100644 index 000000000000..ade2bf9f8772 --- /dev/null +++ b/.changeset/blazing-fires-burn.md @@ -0,0 +1,13 @@ +--- +"@biomejs/biome": patch +--- +[noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) is now able to bind read of value to a type-only import in ambient contexts ([#4526](https://github.com/biomejs/biome/issues/4526)). + +In the following code, `A` is now correctly bound to the type-only import. +Previously, `A` was reported as an undeclared variable. + +```ts +import type { A } from "mod"; + +declare class B extends A {} +``` diff --git a/.changeset/blue-skies-fly.md b/.changeset/blue-skies-fly.md new file mode 100644 index 000000000000..0ef0c5adac85 --- /dev/null +++ b/.changeset/blue-skies-fly.md @@ -0,0 +1,13 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4317](https://github.com/biomejs/biome/issues/4317), setter parameter can contain a trailing comma, the following example will now parsed correctly: + +```ts +export class DummyClass { + set input( + value: string, + ) {} +} +``` diff --git a/.changeset/breezy-horses-fetch.md b/.changeset/breezy-horses-fetch.md new file mode 100644 index 000000000000..30718b890d9f --- /dev/null +++ b/.changeset/breezy-horses-fetch.md @@ -0,0 +1,6 @@ +--- +"@biomejs/biome": minor +--- + +Add a new lint rule `noConstantBinaryExpression`. +This rule is inspired from ESLint's [no-constant-binary-expression](https://eslint.org/docs/latest/rules/no-constant-binary-expression) rule. diff --git a/.changeset/bright-stars-shine.md b/.changeset/bright-stars-shine.md new file mode 100644 index 000000000000..3b95e8d9b9d8 --- /dev/null +++ b/.changeset/bright-stars-shine.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4575](https://github.com/biomejs/biome/issues/4575), don't wrap selector indentation after css comments. diff --git a/.changeset/bright-sun-shines.md b/.changeset/bright-sun-shines.md new file mode 100644 index 000000000000..29bcd067699e --- /dev/null +++ b/.changeset/bright-sun-shines.md @@ -0,0 +1,4 @@ +--- +"@biomejs/biome": patch +--- +Fix [#4258](https://github.com/biomejs/biome/issues/4258), where fixed css parse error with @-moz-document url-prefix(). diff --git a/.changeset/bright-trees-dance.md b/.changeset/bright-trees-dance.md new file mode 100644 index 000000000000..03018f445f27 --- /dev/null +++ b/.changeset/bright-trees-dance.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Don't parse the files that don't end with the json extension as JSON files in the `.vscode` directory ([#4391](https://github.com/biomejs/biome/issues/4391)) diff --git a/.changeset/broken-dreams-fade.md b/.changeset/broken-dreams-fade.md new file mode 100644 index 000000000000..ba8a0bb29fd2 --- /dev/null +++ b/.changeset/broken-dreams-fade.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": minor +--- + +`noUnusedImports` now reports empty named imports and suggests its removal ([#3574](https://github.com/biomejs/biome/issues/3574)). + +The rule now suggests the removal of empty named imports such as: + +```diff +- import {} from "mod"; +``` diff --git a/.changeset/calm-forests-grow.md b/.changeset/calm-forests-grow.md new file mode 100644 index 000000000000..a54c77c9d05c --- /dev/null +++ b/.changeset/calm-forests-grow.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +`biome migrate eslint` now correctly resolves scoped package named `eslint-config` with a path. diff --git a/.changeset/calm-winds-blow.md b/.changeset/calm-winds-blow.md new file mode 100644 index 000000000000..33267a933cf3 --- /dev/null +++ b/.changeset/calm-winds-blow.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#3836](https://github.com/biomejs/biome/issues/3836), css parser allow multiple semicolons after a declaration, the following example will now parsed correctly: + +```css +.foo { + color: red;; +} +``` diff --git a/.changeset/clean-floors-shine.md b/.changeset/clean-floors-shine.md new file mode 100644 index 000000000000..ba9a7de30771 --- /dev/null +++ b/.changeset/clean-floors-shine.md @@ -0,0 +1,18 @@ +--- +"@biomejs/biome": patch +--- +Fix [#4553](https://github.com/biomejs/biome/issues/4553), `noUselessFragments` fix result has invalid syntax for JSX attribute, the follow code will fix: + +```jsx +Loading...}> + {children} +; +``` + +it will fix as: + +```jsx +Loading...}> + {children} +; +``` diff --git a/.changeset/clean-sky-fly.md b/.changeset/clean-sky-fly.md new file mode 100644 index 000000000000..b72c0ca5c19a --- /dev/null +++ b/.changeset/clean-sky-fly.md @@ -0,0 +1,10 @@ +--- +"@biomejs/biome": patch +--- + +`biome migrate eslint` now correctly handles shared ESLint configuration that don't follow the ESLint naming convention ([#4528](https://github.com/biomejs/biome/issues/4528)). + +ESLint recommends that a package that exports a shared configuration be prefixed with `eslint-config-` or simply named `eslint-config`. +This is only a recommendation. +Packages that export shared configurations can have arbitrary names. +Biome is now able to load any package. diff --git a/.changeset/code_actions_via_ideeditor.md b/.changeset/code_actions_via_ideeditor.md new file mode 100644 index 000000000000..030e1720ae60 --- /dev/null +++ b/.changeset/code_actions_via_ideeditor.md @@ -0,0 +1,35 @@ +--- +"@biomejs/biome": minor +--- + +Biome users can now configure code actions from linter rules as well as assist actions directly in the settings of their IDE/editor. + +For example, let's consider the lint rule [`noSwitchDeclarations`](https://biomejs.dev/linter/rules/no-switch-declarations/), which has an unsafe fix. +Previously, if you wanted to use this rule, you were "forced" to enable it via configuration, and if you wanted to apply its fix when you saved a file, you were forced to mark the fix as safe: + +```json +{ + "linter": { + "rules": { + "correctness": { + "noSwitchDeclarations": { + "level": "error", + "fix": "safe" + } + } + } + } +} +``` + +Now, you can benefit from the code action without making the fix safe for the entire project. IDEs and editors that are LSP compatible allow to list a series of "filters" or code actions that can be applied on save. In the case of VS Code, you will need to add the following snippet in the `settings.json`: + +```json +{ + "editor.codeActionsOnSave": { + "quickfix.biome.correctness.noSwitchDeclarations": "explicit" + } +} +``` + +Upon save, Biome will inform the editor the apply the code action of the rule `noSwitchDeclarations`. diff --git a/.changeset/cold-breeze-blows.md b/.changeset/cold-breeze-blows.md new file mode 100644 index 000000000000..b3d358dec796 --- /dev/null +++ b/.changeset/cold-breeze-blows.md @@ -0,0 +1,14 @@ +--- +"@biomejs/biome": minor +--- + +`noUnusedImports` now keeps comments separated from the import with a blank line ([#3401](https://github.com/biomejs/biome/issues/3401)). + +Here is an example: + +```diff + // Orphan comment + +- // Header comment +- import {} from "mod"; +``` diff --git a/.changeset/cold-rains-fall.md b/.changeset/cold-rains-fall.md new file mode 100644 index 000000000000..a30073b7396a --- /dev/null +++ b/.changeset/cold-rains-fall.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4756](https://github.com/biomejs/biome/issues/4756), `noDuplicateProperties` now throws lint errors properly when we use `@supports`. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 000000000000..c54c930db3b9 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", + "changelog": ["@changesets/changelog-github", { "repo": "biomejs/biome" }], + "commit": false, + "fixed": [ + [ + "@biomejs/biome", + "@biomejs/cli-*", + "@biomejs/wasm-*" + ] + ], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [ + "@biomejs/benchmark", + "@biomejs/aria-data", + "tailwindcss-config-analyzer" + ] +} diff --git a/.changeset/cyan-brooms-mate.md b/.changeset/cyan-brooms-mate.md new file mode 100644 index 000000000000..4603cdd50197 --- /dev/null +++ b/.changeset/cyan-brooms-mate.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Formatter option `bracketSpacing` is now also supported in JSON files. diff --git a/.changeset/dark-nights-fall.md b/.changeset/dark-nights-fall.md new file mode 100644 index 000000000000..acc4e1123ab7 --- /dev/null +++ b/.changeset/dark-nights-fall.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +`biome migrate eslint` now correctly handles ESLint configuration with `null` values in file lists ([#4740](https://github.com/biomejs/biome/issues/4740)). diff --git a/.changeset/deep-oceans-wave.md b/.changeset/deep-oceans-wave.md new file mode 100644 index 000000000000..f3e7784dc39c --- /dev/null +++ b/.changeset/deep-oceans-wave.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4202](https://github.com/biomejs/biome/issues/4202), where the formatting of the test function was different from prettier. diff --git a/.changeset/deep-seas-dive.md b/.changeset/deep-seas-dive.md new file mode 100644 index 000000000000..557a30355f06 --- /dev/null +++ b/.changeset/deep-seas-dive.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#342](https://github.com/biomejs/biome/issues/342), js parser handle unterminated `JSX_STRING_LITERAL` properly + +```jsx +function Comp() { + return ( + `: + +```shell +biome explain noVar +``` diff --git a/.changeset/export_named_type_parser.md b/.changeset/export_named_type_parser.md new file mode 100644 index 000000000000..01cb67b97d41 --- /dev/null +++ b/.changeset/export_named_type_parser.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": patch +--- + +Export Named Type support `default` parser. + +The following code is now parsed successfully: + +```ts +export { type A as default } from './b.ts'; +``` diff --git a/.changeset/falling-leaves-turn.md b/.changeset/falling-leaves-turn.md new file mode 100644 index 000000000000..6479af2e93b1 --- /dev/null +++ b/.changeset/falling-leaves-turn.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) no longer suggests renaming top-level variables in a global declaration file. diff --git a/.changeset/fast-rivers-run.md b/.changeset/fast-rivers-run.md new file mode 100644 index 000000000000..458cfa32fa43 --- /dev/null +++ b/.changeset/fast-rivers-run.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4413](https://github.com/biomejs/biome/issues/4413), where the GraphQL formatter adds a new line at the start of block comments on Windows. diff --git a/.changeset/fix_1597_useexhaustivedependencies_now_consider_react_hooks_stable_within_parentheses_or_type_assertions.md b/.changeset/fix_1597_useexhaustivedependencies_now_consider_react_hooks_stable_within_parentheses_or_type_assertions.md new file mode 100644 index 000000000000..281f51da8692 --- /dev/null +++ b/.changeset/fix_1597_useexhaustivedependencies_now_consider_react_hooks_stable_within_parentheses_or_type_assertions.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix #1597, useExhaustiveDependencies now consider React hooks stable within parentheses or type assertions. diff --git a/.changeset/fix_a_bug_where_config_path_accepted_configuration_files_with_unsupported_extensions.md b/.changeset/fix_a_bug_where_config_path_accepted_configuration_files_with_unsupported_extensions.md new file mode 100644 index 000000000000..8a8ab7bd34f1 --- /dev/null +++ b/.changeset/fix_a_bug_where_config_path_accepted_configuration_files_with_unsupported_extensions.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix a bug where `--config-path` accepted configuration files with unsupported extensions. Now only `.json` and `.jsonc` are accepted, and an error is raised otherwise. diff --git a/.changeset/fix_fragament_4751.md b/.changeset/fix_fragament_4751.md new file mode 100644 index 000000000000..bf7e39366cf2 --- /dev/null +++ b/.changeset/fix_fragament_4751.md @@ -0,0 +1,24 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4751](https://github.com/biomejs/biome/issues/4751) by checking fragments inside `JSXElement` and conditional expressions. For example: + +The Case: + +```jsx +
+ <> +
+
+ +
; +``` + +And: + +```jsx +showFullName ? <>{fullName} : <>{firstName}; +``` + +It will report. diff --git a/.changeset/fix_no_fallthrough_switch_case_panic.md b/.changeset/fix_no_fallthrough_switch_case_panic.md new file mode 100644 index 000000000000..0cd7d3327678 --- /dev/null +++ b/.changeset/fix_no_fallthrough_switch_case_panic.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +The rule `noFallthroughSwitchCase` no longer panics on some incomplete code snippets. diff --git a/.changeset/fix_nomissingvarfunction_false_positives_for_container_name.md b/.changeset/fix_nomissingvarfunction_false_positives_for_container_name.md new file mode 100644 index 000000000000..0df2beed5748 --- /dev/null +++ b/.changeset/fix_nomissingvarfunction_false_positives_for_container_name.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#5007](https://github.com/biomejs/biome/issues/5007) `noMissingVarFunction` false positives for `container-name`. diff --git a/.changeset/fix_the_use_strict_directive_insertion_logic_for_shebang_and_top_leading_comments.md b/.changeset/fix_the_use_strict_directive_insertion_logic_for_shebang_and_top_leading_comments.md new file mode 100644 index 000000000000..55e7dff6a1e9 --- /dev/null +++ b/.changeset/fix_the_use_strict_directive_insertion_logic_for_shebang_and_top_leading_comments.md @@ -0,0 +1,47 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4841](https://github.com/biomejs/biome/issues/4841), shebang and top leading comments in cjs files are now handled correctly + +- shebang only (keep it as is) + +``` +#!/usr/bin/env node +``` + +- comments only (keep it as is) + +``` +// comment +``` + +- with shebang + +```diff +- #!/usr/bin/env node"use strict"; ++ #!/usr/bin/env node ++ "use strict"; +let some_variable = "some value"; +``` + +- with comment + +```diff +- // comment +- "use strict"; // comment ++ "use strict"; ++ // comment +let some_variable = "some value"; +``` + +- with shebang and comment + +```diff +- #!/usr/bin/env node"use strict"; +- // comment ++ #!/usr/bin/env node ++ "use strict"; ++ // comment +let some_variable = "some value"; +``` diff --git a/.changeset/fixanalyzer_suppression_comment_fails_with_inner_comments_in_functions.md b/.changeset/fixanalyzer_suppression_comment_fails_with_inner_comments_in_functions.md new file mode 100644 index 000000000000..b96089a50b0c --- /dev/null +++ b/.changeset/fixanalyzer_suppression_comment_fails_with_inner_comments_in_functions.md @@ -0,0 +1,17 @@ +--- +"@biomejs/biome": patch +--- + +Suppression comment should not fail with inner comments in functions. + +The following code: + +```ts +// biome-ignore lint/complexity/useArrowFunction: not work +const foo0 = function (bar: string) { + // biome-ignore lint/style/noParameterAssign: work + bar = "baz"; +}; +``` + +The suppression comment `// biome-ignore lint/style/noParameterAssign: work` will not be invalid. diff --git a/.changeset/fragile-glass-breaks.md b/.changeset/fragile-glass-breaks.md new file mode 100644 index 000000000000..e0ddb3c9fda9 --- /dev/null +++ b/.changeset/fragile-glass-breaks.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class/) no longer panics on malformed escape sequences that end with a multi-byte character ([#4587](https://github.com/biomejs/biome/issues/4587)). diff --git a/.changeset/frozen-lakes-crack.md b/.changeset/frozen-lakes-crack.md new file mode 100644 index 000000000000..95a6dcc284e6 --- /dev/null +++ b/.changeset/frozen-lakes-crack.md @@ -0,0 +1,12 @@ +--- +"@biomejs/biome": minor +--- + +[noUnknownProperty](https://biomejs.dev/linter/rules/no-unknown-property/) now accepts more known CSS properties ([#4549](https://github.com/biomejs/biome/issues/4549)). + +```diff +- ['anchor-default', 'anchor-scroll', 'inset-area', 'position-animation', 'position-fallback', 'position-fallback-bounds', 'position-try-options'] ++ ['anchor-scope', 'interpolate-size', 'line-fit-edge', 'masonry', 'masonry-auto-tracks', 'masonry-direction', 'masonry-fill', 'masonry-flow', 'masonry-slack', 'masonry-template-areas', 'masonry-template-tracks', 'position-anchor', 'position-area', 'position-try-fallbacks', 'position-visibility', 'scroll-start-target', 'text-box', 'view-transition-class', 'view-transition-group'] +``` + +This change replaces deprecated properties, improving CSS validation. diff --git a/.changeset/gentle-breeze-blows.md b/.changeset/gentle-breeze-blows.md new file mode 100644 index 000000000000..039c06c248fb --- /dev/null +++ b/.changeset/gentle-breeze-blows.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4121](https://github.com/biomejs/biome/issues/4326), don't ident a CSS selector when has leading comments. diff --git a/.changeset/gentle-spies-drum.md b/.changeset/gentle-spies-drum.md new file mode 100644 index 000000000000..956b585ee76a --- /dev/null +++ b/.changeset/gentle-spies-drum.md @@ -0,0 +1,20 @@ +--- +"@biomejs/biome": minor +--- + +Introduce a new option `objectWrap` for JS formatter. +It does the same thing as Prettier's [Object Wrap](https://prettier.io/docs/options#object-wrap) option. + +For example, the following code is considered as already formatted when `objectWrap` is `preserve` (default): + +```js +const obj = { + foo: "bar", +}; +``` + +However, when `objectWrap` is `collapse`, it will be formatted to the following output: + +```js +const obj = { foo: "bar" }; +``` diff --git a/.changeset/green-grass-grows.md b/.changeset/green-grass-grows.md new file mode 100644 index 000000000000..444797bbbfae --- /dev/null +++ b/.changeset/green-grass-grows.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#342](https://github.com/biomejs/biome/issues/342), js parser is no longer progressing for an invalid object member name: + +```js +({ + params: { [paramName: string]: number } = {} +}) +``` diff --git a/.changeset/happy-clouds-drift.md b/.changeset/happy-clouds-drift.md new file mode 100644 index 000000000000..7d67fd060012 --- /dev/null +++ b/.changeset/happy-clouds-drift.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4334](https://github.com/biomejs/biome/issues/4334), don't insert trailing comma on type import statement. diff --git a/.changeset/heavy-snows-fall.md b/.changeset/heavy-snows-fall.md new file mode 100644 index 000000000000..2e8b33ba3369 --- /dev/null +++ b/.changeset/heavy-snows-fall.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports/) no longer reports used values imported as types in an external module ([#3895])(https://github.com/biomejs/biome/issues/3895). diff --git a/.changeset/high-mountains-rise.md b/.changeset/high-mountains-rise.md new file mode 100644 index 000000000000..202d8b9fed31 --- /dev/null +++ b/.changeset/high-mountains-rise.md @@ -0,0 +1,9 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#342](https://github.com/biomejs/biome/issues/342), "expected a declaration as guaranteed by is_at_ts_declare_statement" error for declare interface: + +```ts +declare interface +``` diff --git a/.changeset/improve_formatting_of_numbers_in_css.md b/.changeset/improve_formatting_of_numbers_in_css.md new file mode 100644 index 000000000000..d18686b743b1 --- /dev/null +++ b/.changeset/improve_formatting_of_numbers_in_css.md @@ -0,0 +1,14 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#5031](https://github.com/biomejs/biome/issues/5031). Improve CSS formatting for numbers: + +```diff +.class { +- padding: .5em; +- marding: 1.0; ++ padding: 0.5em; ++ marding: 1; +} +``` diff --git a/.changeset/introduce_includes.md b/.changeset/introduce_includes.md new file mode 100644 index 000000000000..753136395f26 --- /dev/null +++ b/.changeset/introduce_includes.md @@ -0,0 +1,44 @@ +--- +"@biomejs/biome": minor +--- + +Introduce `includes`. + +Biome allows users to `include` and `ignore` files in its configuration using glob patterns. + +For example, in the following configuration, all files of the `src/` directory are checked except the ones ending with the extension `.test.js`. + +```json +{ + "files": { + "include": ["src/**"], + "ignore": ["**/*.test.js"] + } +} +``` + +Some Biome users have requested the ability to ignore a set of files except some of the files. +With the current system, this is not possible because `include` is always applied before `ignore`. + +Also, many Biome users [reported](https://github.com/biomejs/biome/issues/2421) [issues](https://github.com/biomejs/biome/issues/3345) with the behavior of the glob patterns. +Notably: + +- `src/**` is interpreted as `**/src/**` +- `*.js` is interpreted as `**/*.js` + +To solve all these issues, we introduce a new field `includes`, which replaces both `include` and `ignore`. +`includes` accepts an array of glob patterns with a stricter and more intuitive behavior than the previous glob pattern format. +A glob starting with a `!` is an exception. +This replaces `ignore` patterns. + +The previous configuration must be updated as follows: + +```json +{ + "files": { + "includes": ["src/**", "!**/*.test.js"] + } +} +``` + +You can run `biome migrate` to automatically convert from `include` and `ignore` to `includes`. diff --git a/.changeset/introduce_the_domains_linter_feature.md b/.changeset/introduce_the_domains_linter_feature.md new file mode 100644 index 000000000000..3f10fa1d8269 --- /dev/null +++ b/.changeset/introduce_the_domains_linter_feature.md @@ -0,0 +1,54 @@ +--- +"@biomejs/biome": minor +--- + +Introduce the `domains` linter feature. The Biome linter now has a new way to opt-in rules, with a concept called `domains`. + +Domains can be seen as concepts shared by different rules. + +You can enable and disable multiple rules that belong to a domain. When you assign `"all"`, Biome will enable all the rules, when you assign `"none"`, Biome will disable the rules, when you assign "recommended", Biome will enable all rules of the domain that are recommended. + +```json5 +// biome.jsonc +{ + "linter": { + "domains": { + "test": "all", // all rules that belong to this domain are enabled + "react": "recommended", // only the recommended rules from this domain are enabled + "solid": "none" // rules related to Solid are disabled + } + } +} +``` + +New domains introduced: + +- `test`: it will enable rules: + - `noExportsInTest` + - `noExcessiveNestedTestSuites` + - `noDuplicateTestHooks` + - `noFocusedTests` + And it will inject the following globals: + - `after` + - `afterAll` + - `afterEach` + - `before` + - `beforeEach` + - `beforeAll` + - `describe` + - `it` + - `expect` + - `test` +- `next`: it will enable rules for Next.js projects: + - `useExhaustiveDependencies` + - `useHookAtTopLevel` + - `noImgElement` + - `noHeadImportInDocument` + - `noHeadImportInDocument` +- `react`: it will enable rules for React projects: + - `useExhaustiveDependencies` + - `useHookAtTopLevel` +- `solid`: it will enable rules for Solid projects: + - `noReactSpecificProps` + +For more information regarding how Biome enables rules via domains, please refer to the documentation page of each rule. diff --git a/.changeset/lemon-flies-jam.md b/.changeset/lemon-flies-jam.md new file mode 100644 index 000000000000..6daba88a91ca --- /dev/null +++ b/.changeset/lemon-flies-jam.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#5053](https://github.com/biomejs/biome/issues/5053), now the rule correctly handles `console.log` inside arrow function expressions. diff --git a/.changeset/lonely-trees-stand.md b/.changeset/lonely-trees-stand.md new file mode 100644 index 000000000000..15e059dead8e --- /dev/null +++ b/.changeset/lonely-trees-stand.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#3229](https://github.com/biomejs/biome/issues/3229), where Biome wasn't idempotent when block comments were placed inside compound selectors. diff --git a/.changeset/lucky-jeans-deliver.md b/.changeset/lucky-jeans-deliver.md new file mode 100644 index 000000000000..423f24fc5b87 --- /dev/null +++ b/.changeset/lucky-jeans-deliver.md @@ -0,0 +1,5 @@ +--- +"@biomejs/backend-jsonrpc": major +--- + +Make the package compatibile with the new Biome APIs. diff --git a/.changeset/mark_useselfclosingelements_as_safe_and_improve_error_message.md b/.changeset/mark_useselfclosingelements_as_safe_and_improve_error_message.md new file mode 100644 index 000000000000..0aecc868d2f2 --- /dev/null +++ b/.changeset/mark_useselfclosingelements_as_safe_and_improve_error_message.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Mark `useSelfClosingElements` as safe and improve error message. diff --git a/.changeset/new_top_level_suppression_for_the_analyzer.md b/.changeset/new_top_level_suppression_for_the_analyzer.md new file mode 100644 index 000000000000..71417bb7d903 --- /dev/null +++ b/.changeset/new_top_level_suppression_for_the_analyzer.md @@ -0,0 +1,60 @@ +--- +"@biomejs/biome": minor +--- + +The Biome analyzer now supports a new top-level suppression. These suppression have to be placed at the top of the file, and they must be followed by two newlines (`\n\n\`). + +The analyzer rules specified inside the block comment will be suppressed for the whole file. + +In the example, we suppress the rules `lint/style/useConst` and `lint/suspicious/noDebugger` for the whole file: + +```js +// main.js +/** + * biome-ignore-all lint/style/useConst: i like let + * biome-ignore-all lint/suspicious/noDebugger: needed now + */ + +let path = "/path"; +let _tmp = undefined; +debugger +``` + +In this other example, we suppress `lint/suspicious/noEmptyBlock` for a whole CSS file: + +```css +/** +/* biome-ignore-all lint/suspicious/noEmptyBlock: it's fine to have empty blocks +*/ + +a {} +span {} +``` + +A new diagnostic is emitted if `biome-ignore-all` suppression isn't placed at the top of the file: + + +```block +file.js:3:1 suppressions/incorrect ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Top level suppressions can only be used at the beginning of the file. + + 2 │ let foo = 2; + > 3 │ /** + │ ^^^ + > 4 │ * biome-ignore-all lint/style/useConst: reason + > 5 │ */ + │ ^^ + 6 │ let bar = 33; + + i Rename this to biome-ignore + + 2 │ let foo = 2; + 3 │ /** + > 4 │ * biome-ignore-all lint/style/useConst: reason + │ ^^^^^^^^^^^^^^^^ + 5 │ */ + 6 │ let bar = 33; + + +``` diff --git a/.changeset/no_more_trailing_commas_in_json_files.md b/.changeset/no_more_trailing_commas_in_json_files.md new file mode 100644 index 000000000000..0aa45692cb88 --- /dev/null +++ b/.changeset/no_more_trailing_commas_in_json_files.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +The Biome formatter doesn't add a trailing command in `.json` files, even when `json.formatter.trailingCommas` is set to `true`. diff --git a/.changeset/old-eels-help.md b/.changeset/old-eels-help.md new file mode 100644 index 000000000000..a6580f596987 --- /dev/null +++ b/.changeset/old-eels-help.md @@ -0,0 +1,6 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4875](https://github.com/biomejs/biome/issues/4875), where the Jetbrains IDE terminal would output not clickable, relative file path link to the diagnostic file. This does not fix paths without line and column numbers. + diff --git a/.changeset/old-towers-stand.md b/.changeset/old-towers-stand.md new file mode 100644 index 000000000000..84c27b4906d0 --- /dev/null +++ b/.changeset/old-towers-stand.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4719](https://github.com/biomejs/biome/issues/4719), `bracketSameLine` now performs as expected when a comment is placed before the last JSX attribute. diff --git a/.changeset/old-woods-stand.md b/.changeset/old-woods-stand.md new file mode 100644 index 000000000000..2a88e2ddfb0a --- /dev/null +++ b/.changeset/old-woods-stand.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Don't panic when a multi-byte character is found in a unicode escape sequence ([#4564](https://github.com/biomejs/biome/issues/4564)). diff --git a/.changeset/orange-gifts-burn.md b/.changeset/orange-gifts-burn.md new file mode 100644 index 000000000000..660e7e720363 --- /dev/null +++ b/.changeset/orange-gifts-burn.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4947](https://github.com/biomejs/biome/issues/4947): the `useTemplate` lint rule now ignores concatenated literals folded to multiple lines. diff --git a/.changeset/peaceful-minds-rest.md b/.changeset/peaceful-minds-rest.md new file mode 100644 index 000000000000..d51a5944d3e4 --- /dev/null +++ b/.changeset/peaceful-minds-rest.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix a panic related to bogus import statements in `useExhaustiveDependencies` ([#4568](https://github.com/biomejs/biome/issues/4568)). diff --git a/.changeset/quiet-mountains-rest.md b/.changeset/quiet-mountains-rest.md new file mode 100644 index 000000000000..59c92ffdc41c --- /dev/null +++ b/.changeset/quiet-mountains-rest.md @@ -0,0 +1,4 @@ +--- +"@biomejs/biome": patch +--- +Fix [#4026](https://github.com/biomejs/biome/issues/4026), don't move comments in `grid-template`. diff --git a/.changeset/quiet-nights-dream.md b/.changeset/quiet-nights-dream.md new file mode 100644 index 000000000000..863e35691466 --- /dev/null +++ b/.changeset/quiet-nights-dream.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix `useSortedClasses` false positive and Supplementary test case ([#3394](https://github.com/biomejs/biome/issues/3394)). diff --git a/.changeset/quiet-streams-flow.md b/.changeset/quiet-streams-flow.md new file mode 100644 index 000000000000..d8d82e787f9a --- /dev/null +++ b/.changeset/quiet-streams-flow.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Don't panic when a declare statement is followed by an unexpected token.([#4562](https://github.com/biomejs/biome/issues/4562)). diff --git a/.changeset/reduced_accepted_values.md b/.changeset/reduced_accepted_values.md new file mode 100644 index 000000000000..f8ff953355f5 --- /dev/null +++ b/.changeset/reduced_accepted_values.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": major +--- + +Reduced accepted values for formatter options: +- The option `--quote-style` doesn't accept `Single` and `Double` anymore. +- The option `--quote-properties` doesn't accept `AsNeeded` and `Preserve` anymore. +- The option `--semicolons` doesn't accept `AsNeeded` and `Always` anymore. +- The option `--arrow-parenthesis` doesn't accept `AsNeeded` and `Always` anymore. +- The option `--trailing-commas` doesn't accept `ES5`, `All` and `None` anymore. +- The option `--attribute-position` doesn't accept `Single` and `Multiline` anymore. diff --git a/.changeset/remove_biome_log_dir.md b/.changeset/remove_biome_log_dir.md new file mode 100644 index 000000000000..724e1f6d3e18 --- /dev/null +++ b/.changeset/remove_biome_log_dir.md @@ -0,0 +1,9 @@ +--- +"@biomejs/biome": major +--- + +Remove `BIOME_LOG_DIR`. + +The environment variable `BIOME_LOG_DIR` isn't supported anymore. + +Use `BIOME_LOG_PATH` instead. diff --git a/.changeset/remove_deprecaterd_rules.md b/.changeset/remove_deprecaterd_rules.md new file mode 100644 index 000000000000..66b9574e52a3 --- /dev/null +++ b/.changeset/remove_deprecaterd_rules.md @@ -0,0 +1,15 @@ +--- +"@biomejs/biome": major +--- + +Remove deprecated rules. + +The following _deprecated_ rules have been deleted: + +- `noInvalidNewBuiltin` +- `noNewSymbol` +- `useShorthandArrayType` +- `useSingleCaseStatement` +- `noConsoleLog` + +Run the command `biome migrate --write` to update the configuration. diff --git a/.changeset/remove_indentsize_option.md b/.changeset/remove_indentsize_option.md new file mode 100644 index 000000000000..2f880776a38f --- /dev/null +++ b/.changeset/remove_indentsize_option.md @@ -0,0 +1,15 @@ +--- +"@biomejs/biome": major +--- + +Remove `indentSize` deprecated option. + +The deprecated option `indentSize`, and its relative CLI options, has been removed: +- Configuration file: `formatter.indentSize` +- Configuration file: `javascript.formatter.indentSize` +- Configuration file: `json.formatter.indentSize` +- CLI option `--indent-size` +- CLI option `--javascript-formatter-indent-size` +- CLI option `--json-formatter-indent-size` + +Use `indentWidth` and its relative CLI options instead. diff --git a/.changeset/remove_rome_binary.md b/.changeset/remove_rome_binary.md new file mode 100644 index 000000000000..1227e32d7790 --- /dev/null +++ b/.changeset/remove_rome_binary.md @@ -0,0 +1,7 @@ +--- +"@biomejs/biome": major +--- + +Remove `ROME_BINARY`. Use `BIOME_BINARY` instead. + + diff --git a/.changeset/remove_support_for_legacy_suppressions.md b/.changeset/remove_support_for_legacy_suppressions.md new file mode 100644 index 000000000000..4ba759773018 --- /dev/null +++ b/.changeset/remove_support_for_legacy_suppressions.md @@ -0,0 +1,13 @@ +--- +"@biomejs/biome": major +--- + +Remove support for legacy suppressions. + +Biome used to support "legacy suppressions" that looked like this: + +```js +// biome-ignore lint(complexity/useWhile): reason +``` + +This format is no longer supported. diff --git a/.changeset/remove_support_for_max_line_length_from_editorconfig_as_it_isnt_part_of_the_official_spec_anymore_.md b/.changeset/remove_support_for_max_line_length_from_editorconfig_as_it_isnt_part_of_the_official_spec_anymore_.md new file mode 100644 index 000000000000..6b3965e195ff --- /dev/null +++ b/.changeset/remove_support_for_max_line_length_from_editorconfig_as_it_isnt_part_of_the_official_spec_anymore_.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +Remove support for `max_line_length` from `.editorconfig`, as it isn't part of the official spec anymore. diff --git a/.changeset/remove_support_for_rome_ignore_suppression_comment.md b/.changeset/remove_support_for_rome_ignore_suppression_comment.md new file mode 100644 index 000000000000..6d5fce844f8e --- /dev/null +++ b/.changeset/remove_support_for_rome_ignore_suppression_comment.md @@ -0,0 +1,7 @@ +--- +"@biomejs/biome": major +--- + +Remove support for `rome-ignore` suppression comment. + +Use the `biome-ignore` suppression comment instead. diff --git a/.changeset/remove_support_for_romejson.md b/.changeset/remove_support_for_romejson.md new file mode 100644 index 000000000000..817626dcd737 --- /dev/null +++ b/.changeset/remove_support_for_romejson.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +Remove support for `rome.json`. diff --git a/.changeset/remove_the_option_all_from_the_linter.md b/.changeset/remove_the_option_all_from_the_linter.md new file mode 100644 index 000000000000..2dd50bc4fe36 --- /dev/null +++ b/.changeset/remove_the_option_all_from_the_linter.md @@ -0,0 +1,16 @@ +--- +"@biomejs/biome": major +--- + +Remove the option `all` from the linter. + +The options `linter.rules.all` and `linter.rules..all` has been removed. + +The number of rules in Biome have increased in scope and use cases, and sometimes some of them can conflict with each other. + +The option was useful at the beginning, but now it's deemed harmful, because it can unexpected behaviours in users projects. + +To automatically remove it, run the following command: +```shell +biome migrate --write +``` diff --git a/.changeset/remove_trailingcomma.md b/.changeset/remove_trailingcomma.md new file mode 100644 index 000000000000..fecf99930666 --- /dev/null +++ b/.changeset/remove_trailingcomma.md @@ -0,0 +1,21 @@ +--- +"@biomejs/biome": major +--- + +Removed the option `trailingComma` from the configuration and the CLI. Use the option `trailingCommas` instead: + +```diff +{ + "javascript": { + "formatter": { +- "trailingComma": "es5" ++ "trailingCommas": "es5" + } + } +} +``` + +```diff +-biome format --trailing-comma=es5 ++biome format --trailing-commas=es5 +``` diff --git a/.changeset/removed_apply_and_apply_unsafe.md b/.changeset/removed_apply_and_apply_unsafe.md new file mode 100644 index 000000000000..58d6558e5156 --- /dev/null +++ b/.changeset/removed_apply_and_apply_unsafe.md @@ -0,0 +1,17 @@ +--- +"@biomejs/biome": major +--- + +Removed `--apply` and `--apply-unsafe`. + +The CLI options `--apply` and `--apply-unasfe` aren't accepted anymore. Use `--write` and `--write --unafe` instead: + +```diff +-biome check --apply-unsafe ++biome check --write --unsafe +``` + +```diff +-biome check --apply ++biome check --write +``` diff --git a/.changeset/removed_support_for_assert.md b/.changeset/removed_support_for_assert.md new file mode 100644 index 000000000000..0763f0d03461 --- /dev/null +++ b/.changeset/removed_support_for_assert.md @@ -0,0 +1,15 @@ +--- +"@biomejs/biome": major +--- + +Removed support for `assert` syntax. + +Biome now longer supports the `assert` syntax, use the new `with` syntax instead + +```diff +-import {test} from "foo.json" assert { for: "for" } +-export * from "mod" assert { type: "json" } ++import {test} from "foo.json" with { for: "for" } ++export * from "mod" with { type: "json" } +``` + diff --git a/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md b/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md new file mode 100644 index 000000000000..192fdd2cc5bd --- /dev/null +++ b/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md @@ -0,0 +1,8 @@ +--- +"@biomejs/biome": major +--- + +The rule `useImportRestrictions` has been renamed to `noPackagePrivateImports`. + +To avoid confusion with `noRestrictedImports`, `useImportRestrictions` has been +renamed to `noPackagePrivateImports`. diff --git a/.changeset/reworked_how_large_files_behave.md b/.changeset/reworked_how_large_files_behave.md new file mode 100644 index 000000000000..308064a8a3a9 --- /dev/null +++ b/.changeset/reworked_how_large_files_behave.md @@ -0,0 +1,7 @@ +--- +"@biomejs/biome": major +--- + +Previously, files that should exceed the configured size limit would throw an error, and the CLI would exit with an error code. + +Now, the CLI ignores the file, emits a *information* diagnostic and doesn't exit with an error code. diff --git a/.changeset/rising-waves-crash.md b/.changeset/rising-waves-crash.md new file mode 100644 index 000000000000..0176c9e47bfc --- /dev/null +++ b/.changeset/rising-waves-crash.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[noLabelWithoutControl](https://biomejs.dev/linter/rules/no-label-without-control/) detects button tags as input ([#4511])(https://github.com/biomejs/biome/issues/4511). diff --git a/.changeset/sharp-knives-cut.md b/.changeset/sharp-knives-cut.md new file mode 100644 index 000000000000..3bfde7b33d93 --- /dev/null +++ b/.changeset/sharp-knives-cut.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Add `RegExpStringIterator` to the analyzer globals. diff --git a/.changeset/shining-moons-glow.md b/.changeset/shining-moons-glow.md new file mode 100644 index 000000000000..bdb334537b05 --- /dev/null +++ b/.changeset/shining-moons-glow.md @@ -0,0 +1,4 @@ +--- +"@biomejs/biome": patch +--- +[noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) now handles `JsxAttributeInitializerClause`, ensuring that fragments inside expressions like `
/>` are preserved. ([#4208](https://github.com/biomejs/biome/issues/4208)). diff --git a/.changeset/shining-stars-glow.md b/.changeset/shining-stars-glow.md new file mode 100644 index 000000000000..6bf49fa22e6d --- /dev/null +++ b/.changeset/shining-stars-glow.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4533](https://github.com/biomejs/biome/issues/4533), don't throw error when pseudo class after a webkit scrollbar pseudo element. + +The following code will not report: + +```css +::-webkit-scrollbar-thumb:hover {} +``` diff --git a/.changeset/smooth-roads-glide.md b/.changeset/smooth-roads-glide.md new file mode 100644 index 000000000000..0ecac83e4a38 --- /dev/null +++ b/.changeset/smooth-roads-glide.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix [#4323](https://github.com/biomejs/biome/issues/4258), where `lint/a11y/useSemanticElement` accidentally showed recommendations for `role="searchbox"` instead of `role="search"`. diff --git a/.changeset/soft-beds-rest.md b/.changeset/soft-beds-rest.md new file mode 100644 index 000000000000..d9e38e234f95 --- /dev/null +++ b/.changeset/soft-beds-rest.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[noControlCharactersInRegex](https://biomejs.dev/linter/rules/no-control-characters-in-regex) no longer panics when it encounters an unterminated unicode escape sequence ([#4565](https://github.com/biomejs/biome/issues/4565)). diff --git a/.changeset/spotty-hounds-tie.md b/.changeset/spotty-hounds-tie.md new file mode 100644 index 000000000000..dffc001b3d51 --- /dev/null +++ b/.changeset/spotty-hounds-tie.md @@ -0,0 +1,5 @@ +--- +"@biomejs/js-api": patch +--- + +Remove wrong `openProject()` definition, and add JsDoc. diff --git a/.changeset/style_rules_arent_recommended_anymore_.md b/.changeset/style_rules_arent_recommended_anymore_.md new file mode 100644 index 000000000000..3e42bc5e259e --- /dev/null +++ b/.changeset/style_rules_arent_recommended_anymore_.md @@ -0,0 +1,34 @@ +--- +"@biomejs/biome": major +--- + +The `style` rules aren't recommended anymore. + +Linting rules that belong to the group `style` aren't recommended anymore. Here's the list of rules that aren't recommended anymore: + +- `useNumberNamespace` +- `noNonnullAssertion` +- `useAsConstAssertion` +- `noParameterAssign` +- `noInferrableTypes` +- `useNodejsImportProtocol` +- `useExportType` +- `useDefaultParameterLast` +- `noUnusedTemplateLiteral` +- `useExponentiationOperator` +- `useEnumInitializers` +- `useShorthandFunctionType` +- `useLiteralEnumMembers` +- `noVar` +- `noUselessElse` +- `useNumericLiterals` +- `noCommaOperator` +- `useConst` +- `noArguments` +- `useSelfClosingElements` +- `useImportType` +- `useTemplate` +- `useSingleVarDeclarator` +- `useWhile` + +Use `biome migrate` to enable these rules, to avoid breaking changes. diff --git a/.changeset/sweet-fruits-ripen.md b/.changeset/sweet-fruits-ripen.md new file mode 100644 index 000000000000..0d8bb3962fbd --- /dev/null +++ b/.changeset/sweet-fruits-ripen.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +`useExportType` and `useImportType` now ignore TypeScript declaration files ([#4416](https://github.com/biomejs/biome/pull/4416)). diff --git a/.changeset/tall-hills-rise.md b/.changeset/tall-hills-rise.md new file mode 100644 index 000000000000..683ffc33ca6f --- /dev/null +++ b/.changeset/tall-hills-rise.md @@ -0,0 +1,15 @@ +--- +"@biomejs/biome": patch +--- + +[useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/) now reports all expressions using the `Array` constructors. + +Previously, the rule reported only use of the `Array` constructor in expressions statements. + +```js +// This was reported +new Array(); +// This was not reported +const xs = new Array(); +``` + diff --git a/.changeset/the_action_quickfixsuppressrule_is_removed.md b/.changeset/the_action_quickfixsuppressrule_is_removed.md new file mode 100644 index 000000000000..939471000bd0 --- /dev/null +++ b/.changeset/the_action_quickfixsuppressrule_is_removed.md @@ -0,0 +1,35 @@ +--- +"@biomejs/biome": major +--- + +Remove the code action `quickfix.suppressRule`. + +The code action `quickfix.suppressRule` was removed in favour of two new code actions: + +- `quickfix.suppressRule.inline.biome`: a code action that adds a suppression comment for each violation. +- `quickfix.suppressRule.topLevel.biome`: a code action that adds a suppression comment at the top of the file which suppresses a rule for the whole file. + + +Given the following code +```js +let foo = "one"; +debugger +``` + +The code action `quickfix.suppressRule.inline.biome` will result in the following code: +```js +// biome-ignore lint/style/useConst: +let foo = "one"; +// biome-ignore lint/suspicious/noDebugger: +debugger +``` + +The code action `quickfix.suppressRule.topLevel.biome`, instead, will result in the following code: +```js +/** biome-ignore lint/suspicious/noDebugger: */ +/** biome-ignore lint/style/useConst: */ + +let foo = "one"; +debugger; +``` + diff --git a/.changeset/the_file_packagejson.md b/.changeset/the_file_packagejson.md new file mode 100644 index 000000000000..ad79817a0abd --- /dev/null +++ b/.changeset/the_file_packagejson.md @@ -0,0 +1,17 @@ +--- +"@biomejs/biome": major +--- + +Changed default formatting of `package.json`. + +When Biome encounters a file called `package.json`, by default it will format the file with all objects and arrays expanded. + +```diff +- { "name": "project", "dependencies": { "foo": "latest" } } ++ { ++ "projectName": "project", ++ "dependencies": { ++ "foo": "^1.0.0" ++ } ++ } +``` diff --git a/.changeset/the_organizeimports_is_now_part_of_biome_assist.md b/.changeset/the_organizeimports_is_now_part_of_biome_assist.md new file mode 100644 index 000000000000..53ca8bc37772 --- /dev/null +++ b/.changeset/the_organizeimports_is_now_part_of_biome_assist.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +The `organizeImports` is now part of Biome Assist diff --git a/.changeset/the_rule_noconsolelog_has_been_removed.md b/.changeset/the_rule_noconsolelog_has_been_removed.md new file mode 100644 index 000000000000..fbbef0f6277d --- /dev/null +++ b/.changeset/the_rule_noconsolelog_has_been_removed.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +The rule `noConsoleLog` has been removed diff --git a/.changeset/the_rule_novar_now_belongs_to_the_suspicious_group.md b/.changeset/the_rule_novar_now_belongs_to_the_suspicious_group.md new file mode 100644 index 000000000000..361307e82732 --- /dev/null +++ b/.changeset/the_rule_novar_now_belongs_to_the_suspicious_group.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +The rule `noVar` now belongs to the `suspicious` group diff --git a/.changeset/the_rule_useexhaustivedependencies_isnt_recommended_anymore.md b/.changeset/the_rule_useexhaustivedependencies_isnt_recommended_anymore.md new file mode 100644 index 000000000000..67c95af30139 --- /dev/null +++ b/.changeset/the_rule_useexhaustivedependencies_isnt_recommended_anymore.md @@ -0,0 +1,19 @@ +--- +"@biomejs/biome": major +--- + +The rule `useExhaustiveDependencies` is not recommended anymore. If your codebase uses `react` and relies on that rule, you have to enable it: + + +```jsonc +// biome.json +{ + "linter": { + "rules": { + "correctness": { + "useExhaustiveDependencies": "error" + } + } + } +} +``` diff --git a/.changeset/the_rule_usewhile_now_belongs_to_the_complexity_group.md b/.changeset/the_rule_usewhile_now_belongs_to_the_complexity_group.md new file mode 100644 index 000000000000..600a2e8578b2 --- /dev/null +++ b/.changeset/the_rule_usewhile_now_belongs_to_the_complexity_group.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": major +--- + +The rule `useWhile` now belongs to the `complexity` group diff --git a/.changeset/tsconfigjson_files_will_now_be_treated_the_same_as_tsconfigjson_files.md b/.changeset/tsconfigjson_files_will_now_be_treated_the_same_as_tsconfigjson_files.md new file mode 100644 index 000000000000..3bc44ea008da --- /dev/null +++ b/.changeset/tsconfigjson_files_will_now_be_treated_the_same_as_tsconfigjson_files.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +`tsconfig.*.json` files will now be treated the same as `tsconfig.json` files. diff --git a/.changeset/use_new_workspace_apis.md b/.changeset/use_new_workspace_apis.md new file mode 100644 index 000000000000..d71dfffcac13 --- /dev/null +++ b/.changeset/use_new_workspace_apis.md @@ -0,0 +1,6 @@ +--- +"@biomejs/js-api": minor +"@biomejs/biome": minor +--- + +The package now requires `v2` of the WebAssembly packages. The internal APIs of Workspace are now `camelCase`. diff --git a/.changeset/usealttext_dont_check_object_spread.md b/.changeset/usealttext_dont_check_object_spread.md new file mode 100644 index 000000000000..0f40c69784ac --- /dev/null +++ b/.changeset/usealttext_dont_check_object_spread.md @@ -0,0 +1,11 @@ +--- +"@biomejs/biome": major +--- + +The rule `lint/a11y/useAltText` doesn't check the element's attributes containing object spread. + +The following code doesn't trigger the rule anymore: + +```jsx +{alt} +``` diff --git a/.changeset/usefilenamingconvention_and_usenamingconvention_now_require_ascii_names_by_default.md b/.changeset/usefilenamingconvention_and_usenamingconvention_now_require_ascii_names_by_default.md new file mode 100644 index 000000000000..47e01c88929b --- /dev/null +++ b/.changeset/usefilenamingconvention_and_usenamingconvention_now_require_ascii_names_by_default.md @@ -0,0 +1,37 @@ +--- +"@biomejs/biome": major +--- + +Prior to Biome 2.0, non-ASCII names were accepted by default. +They are now rejected. + +For example, the following code is now reported as invalid by the `useNamingConvention` rule. + +```js +let johnCafé; +``` + +If you want to allow non ASCII filenames and non-ASCII identifiers, you need to set the `requireAscii` options in your Biome configuration file to `false`: + +```json +{ + "linter": { + "rules": { + "style": { + "useFilenamingConvention": { + "level": "on", + "options": { + "requireAscii": false + } + } + "useFilenamingConvention": { + "level": "on", + "options": { + "requireAscii": false + } + } + } + } + } +} +``` diff --git a/.changeset/usenamingconvention_preserves_capitalization.md b/.changeset/usenamingconvention_preserves_capitalization.md new file mode 100644 index 000000000000..8b827a3fa621 --- /dev/null +++ b/.changeset/usenamingconvention_preserves_capitalization.md @@ -0,0 +1,29 @@ +--- +"@biomejs/biome": patch +--- + +The `useNamingConvention` rule now suggests a rename that preserves uppercase if possible. + +For instance, Biome suggested renaming `HTMLWrapper` as `htmlWrapper`: + +```diff +- import HTMLWrapper from "HTMLWrapper.tsx"; ++ import htmlWrapper from "HTMLWrapper.tsx"; + + function component() { +- return ; ++ return ; + } +``` + +Since both `PascalCase` and `CamelCase` are accepted, Biome now suggests renaming `HTMLWrapper` as `HtmlWrapper`: + +```diff +- import HTMLWrapper from "HTMLWrapper.tsx"; ++ import HtmlWrapper from "HTMLWrapper.tsx"; + + function component() { +- return ; ++ return ; + } +``` diff --git a/.changeset/violet-dingos-shake.md b/.changeset/violet-dingos-shake.md new file mode 100644 index 000000000000..f80f4f39bbd2 --- /dev/null +++ b/.changeset/violet-dingos-shake.md @@ -0,0 +1,8 @@ +--- +"@biomejs/wasm-web": patch +"@biomejs/wasm-nodejs": patch +"@biomejs/wasm-bundler": patch +"@biomejs/js-api": patch +--- + +Fixed the type definition of `IFileFeaturesResult.featuresSupported` diff --git a/.changeset/warm-sands-shift.md b/.changeset/warm-sands-shift.md new file mode 100644 index 000000000000..74f96b8a8917 --- /dev/null +++ b/.changeset/warm-sands-shift.md @@ -0,0 +1,12 @@ +--- +"@biomejs/biome": minor +--- +[useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/) now provides a code fix. + +```diff +- const xs = new Array(); ++ const xs = []; +``` + +The code fix is currently marked as unsafe. +We plan to make it safe in a future release of Biome. diff --git a/.changeset/wet-hornets-sparkle.md b/.changeset/wet-hornets-sparkle.md new file mode 100644 index 000000000000..92c6da2a67be --- /dev/null +++ b/.changeset/wet-hornets-sparkle.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fix Biome being unable to parse `insert_final_newline = unset` in EditorConfig files. diff --git a/.changeset/white-clouds-move.md b/.changeset/white-clouds-move.md new file mode 100644 index 000000000000..9f5027754dd9 --- /dev/null +++ b/.changeset/white-clouds-move.md @@ -0,0 +1,16 @@ +--- +"@biomejs/biome": patch +--- + +[useArrowFunction](https://biomejs.dev/linter/rules/use-arrow-function/) now preserves directives ([#4530](https://github.com/biomejs/biome/issues/4530)). + +Previously the rule removed the directives when a function expression was turned into an arrow function. +The rule now correctly keeps the directives. + +```diff +- const withDirective = function () { ++ const withDirective = () => { + "use server"; + return 0; + } +``` diff --git a/.changeset/windy-days-pass.md b/.changeset/windy-days-pass.md new file mode 100644 index 000000000000..c4275d6bf336 --- /dev/null +++ b/.changeset/windy-days-pass.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +[useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) now suggests code fixes that match the JSX quote style of the formatter ([#4855](https://github.com/biomejs/biome/issues/4855)). diff --git a/.gitattributes b/.gitattributes index bbd8e76b00ad..e9bcd97d8d38 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,26 +4,26 @@ /crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs linguist-generated=true text=auto eol=lf /crates/biome_configuration/src/generated.rs linguist-generated=true text=auto eol=lf /crates/biome_configuration/src/analyzer/linter/rules.rs linguist-generated=true text=auto eol=lf -/crates/biome_configuration/src/analyzer/assists/actions.rs linguist-generated=true text=auto eol=lf +/crates/biome_configuration/src/analyzer/assist/actions.rs linguist-generated=true text=auto eol=lf /crates/biome_configuration/src/analyzer/parse/rules.rs linguist-generated=true text=auto eol=lf # GraphQL -/crates/biome_graphql_analyze/src/{lint,assists,syntax}.rs linguist-generated=true text=auto eol=lf -/crates/biome_graphql_analyze/src/{lint,assists,syntax}/*.rs linguist-generated=true text=auto eol=lf +/crates/biome_graphql_analyze/src/{lint,assist,syntax}.rs linguist-generated=true text=auto eol=lf +/crates/biome_graphql_analyze/src/{lint,assist,syntax}/*.rs linguist-generated=true text=auto eol=lf /crates/biome_graphql_analyze/src/options.rs linguist-generated=true text=auto eol=lf /crates/biome_graphql_analyze/src/registry.rs linguist-generated=true text=auto eol=lf # CSS -/crates/biome_css_analyze/src/{lint,assists,syntax}.rs linguist-generated=true text=auto eol=lf -/crates/biome_css_analyze/src/{lint,assists,syntax}/*.rs linguist-generated=true text=auto eol=lf +/crates/biome_css_analyze/src/{lint,assist,syntax}.rs linguist-generated=true text=auto eol=lf +/crates/biome_css_analyze/src/{lint,assist,syntax}/*.rs linguist-generated=true text=auto eol=lf /crates/biome_css_analyze/src/options.rs linguist-generated=true text=auto eol=lf /crates/biome_css_analyze/src/registry.rs linguist-generated=true text=auto eol=lf # JSON -/crates/biome_json_analyze/src/{lint,assists,syntax}.rs linguist-generated=true text=auto eol=lf -/crates/biome_json_analyze/src/{lint,assists,syntax}/*.rs linguist-generated=true text=auto eol=lf +/crates/biome_json_analyze/src/{lint,assist,syntax}.rs linguist-generated=true text=auto eol=lf +/crates/biome_json_analyze/src/{lint,assist,syntax}/*.rs linguist-generated=true text=auto eol=lf /crates/biome_js_analyze/src/options.rs linguist-generated=true text=auto eol=lf /crates/biome_js_analyze/src/registry.rs linguist-generated=true text=auto eol=lf # JS -/crates/biome_js_analyze/src/{lint,assists,syntax}.rs linguist-generated=true text=auto eol=lf -/crates/biome_js_analyze/src/{lint,assists,syntax}/*.rs linguist-generated=true text=auto eol=lf +/crates/biome_js_analyze/src/{lint,assist,syntax}.rs linguist-generated=true text=auto eol=lf +/crates/biome_js_analyze/src/{lint,assist,syntax}/*.rs linguist-generated=true text=auto eol=lf /crates/biome_js_analyze/src/options.rs linguist-generated=true text=auto eol=lf /crates/biome_js_analyze/src/registry.rs linguist-generated=true text=auto eol=lf # Grit diff --git a/.github/actions/build-cli/action.yml b/.github/actions/build-cli/action.yml new file mode 100644 index 000000000000..c8e4aa5285a7 --- /dev/null +++ b/.github/actions/build-cli/action.yml @@ -0,0 +1,83 @@ +name: Build Biome CLI +description: Build the Biome CLI +inputs: + version: + description: The version of Biome + required: true + default: "0.0.0" + code-target: + description: The OS code target + required: true + target: + description: The OS target + required: true + os: + description: The OS name + required: true + + +runs: + using: composite + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Install Node.js + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: 20 + + - name: Install Rust toolchain + run: rustup target add ${{ inputs.target }} + + - name: Install arm64 toolchain + if: inputs.code-target == 'linux-arm64' || inputs.code-target == 'linux-arm64-musl' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + + - name: Install musl toolchain + if: inputs.code-target == 'linux-x64-musl' || inputs.code-target == 'linux-arm64-musl' + run: | + sudo apt-get update + sudo apt-get install -y musl-tools + + - name: Audit crates.io dependencies + if: inputs.code-target == 'linux-x64' + run: cargo audit + + - name: Set jemalloc page size for linux-arm64 + if: inputs.code-target == 'linux-arm64' + run: | + echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV + + # Build the CLI binary + - name: Build binaries + run: cargo build -p biome_cli --release --target ${{ inputs.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-gnu-gcc + # Strip all debug symbols from the resulting binaries + RUSTFLAGS: "-C strip=symbols -C codegen-units=1" + # Inline the version of the npm package in the CLI binary + BIOME_VERSION: ${{ inputs.version }} + + # Copy the CLI binary and rename it to include the name of the target platform + - name: Copy CLI binary + if: inputs.os == 'windows-2022' + run: | + mkdir dist + cp target/${{ inputs.target }}/release/biome.exe ./dist/biome-${{ inputs.code-target }}.exe + - name: Copy CLI binary + if: inputs.os != 'windows-2022' + run: | + mkdir dist + cp target/${{ inputs.target }}/release/biome ./dist/biome-${{ inputs.code-target }} + + # Upload the CLI binary as a build artifact + - name: Upload CLI artifact + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: cli-${{ inputs.target }} + path: ./dist/biome-* + if-no-files-found: error diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml deleted file mode 100644 index 6beb6f6b6db9..000000000000 --- a/.github/workflows/prepare_release.yml +++ /dev/null @@ -1,30 +0,0 @@ -on: - push: - branches: [ main ] -name: Prepare Release PR - -permissions: - actions: write - contents: write - pull-requests: write - -jobs: - prepare-release: - if: "!contains(github.event.head_commit.message, 'chore: prepare release')" # Skip merges from releases - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Configure Git - run: | - git config --global user.name GitHub Actions - git config user.email github-actions@github.com - - uses: knope-dev/action@407e9ef7c272d2dd53a4e71e39a7839e29933c48 # v2.1.0 - with: - version: 0.16.2 - github-token: ${{ secrets.GITHUB_TOKEN }} - - run: knope prepare-release --verbose - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: true diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 000000000000..7ceabaa37da9 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,114 @@ +name: Preview releases +on: + workflow_dispatch: + +jobs: + build-binaries: + strategy: + matrix: + include: + - os: windows-2022 + target: x86_64-pc-windows-msvc + code-target: win32-x64 + - os: windows-2022 + target: aarch64-pc-windows-msvc + code-target: win32-arm64 + - os: ubuntu-20.04 + target: x86_64-unknown-linux-gnu + code-target: linux-x64 + - os: ubuntu-20.04 + target: aarch64-unknown-linux-gnu + code-target: linux-arm64 + - os: ubuntu-20.04 + target: x86_64-unknown-linux-musl + code-target: linux-x64-musl + - os: ubuntu-20.04 + target: aarch64-unknown-linux-musl + code-target: linux-arm64-musl + - os: macos-14 + target: x86_64-apple-darwin + code-target: darwin-x64 + - os: macos-14 + target: aarch64-apple-darwin + code-target: darwin-arm64 + + name: Package ${{ matrix.code-target }} + runs-on: ${{ matrix.os }} + + steps: + - name: Run build + uses: ./.github/actions/build + with: + os: ${{ matrix.os }} + target: ${{ matrix.target }} + code-target: ${{ matrix.code-target }} + version: ${{ env.cli-version }} + + publish: + name: Publish + runs-on: ubuntu-latest + needs: + - build-binaries + environment: npm-publish + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Download CLI artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + pattern: cli-* + merge-multiple: true + - name: Download WASM artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + pattern: wasm-* + merge-multiple: true + path: packages/@biomejs + + - name: Install Node.js + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' + + - name: Pin Corepack 0.20 + run: | + echo "Before: corepack => $(corepack --version || echo 'not installed')" + npm install -g corepack@0.20 + echo "After : corepack => $(corepack --version)" + corepack enable + pnpm --version + + - name: Generate npm packages + run: node packages/@biomejs/biome/scripts/generate-packages.mjs + + - name: Publish version + run: | + pnpx pkg-pr-new publish --compact \ + './packages/@biomejs/biome' \ + './packages/@biomejs/cli-*' \ + './packages/@biomejs/wasm-*' \ + --json output.json --comment=off + + - name: Read published version + uses: actions/github-script@v7 + id: version-msg + with: + script: | + const fs = require('fs'); + const output = JSON.parse(fs.readFileSync('output.json', 'utf8')); + const biomePackage = output.packages + .find((p) => p.name === "@biomejs/biome"); + + return "New preview release available for `@biomejs/biome`. Install using the following command:\n\n ```bash\n npm i " + biomePackage.url + "\n```"; + + result-encoding: string + + - name: Send Discord message + uses: tsickert/discord-webhook@v6.0.0 + with: + webhook-url: ${{ secrets.DISCORD_PREVIEW_RELEASES_HOOK }} + content: ${{ steps.version-msg.outputs.result }} diff --git a/.github/workflows/pull_request_js.yml b/.github/workflows/pull_request_js.yml index 79bf15edb213..c601cd6758e2 100644 --- a/.github/workflows/pull_request_js.yml +++ b/.github/workflows/pull_request_js.yml @@ -6,9 +6,9 @@ on: - main - next paths: # Only run when changes are made to js code - - 'editors/**' - # - 'crates/**' - - 'packages/@biomejs/js-api/**' + - 'packages/@biomejs/**' + - 'packages/aria-data/**' + - 'packages/tailwindcss-config-analyzer/**' # Cancel jobs when the PR is updated concurrency: @@ -36,7 +36,7 @@ jobs: restore-keys: | ${{ runner.os }}- - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - - name: Run Biome Format + - name: Run Biome CI check run: | pnpm i pnpm run ci diff --git a/.github/workflows/release_knope.yml b/.github/workflows/release.yml similarity index 52% rename from .github/workflows/release_knope.yml rename to .github/workflows/release.yml index 805dcf803b76..4b0853260a6b 100644 --- a/.github/workflows/release_knope.yml +++ b/.github/workflows/release.yml @@ -1,37 +1,80 @@ name: Release on: - pull_request: - types: [ closed ] - branches: [ main ] + push: + branches: + - main +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + actions: write + contents: write + pull-requests: write jobs: - retrieve-version: - if: github.head_ref == 'release/automated-ci' && github.event.pull_request.merged == true - name: Retrieve version + changesets: + name: Release runs-on: ubuntu-latest - outputs: - version: ${{ env.version }} - version_changed: ${{ steps.version.outputs.changed }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: Pin Corepack 0.20 + run: | + echo "Before: corepack => $(corepack --version || echo 'not installed')" + npm install -g corepack@0.20 + echo "After : corepack => $(corepack --version)" + corepack enable + pnpm --version + + - name: Create Release Pull Request + uses: changesets/action@v1 + with: + version: pnpm run version + commit: "ci: release" + title: "ci: release" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Retrieve version of `@biomejs/biome` and `@biomejs/js-api` + version: + needs: changesets + if: steps.changesets.outputs.hasChangesets == 'false' && github.head_ref == 'changeset-release/main' + outputs: + cli-version: ${{ env.cli-version }} + js-api-version: ${{ env.cli-js-api-version }} - - name: Check version changes + steps: + - name: Check CLI version changes uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5 - id: version + id: cli-version-changed with: diff-search: true file-name: packages/@biomejs/biome/package.json - - name: Set version name - if: steps.version.outputs.changed == 'true' + - name: Check JS API version changes + uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5 + id: js-api-version-changed + with: + diff-search: true + file-name: packages/@biomejs/js-api/package.json + + - name: Set Biome Version as Environment Variable run: | - echo "Version change found! New version: ${{ steps.version.outputs.version }} (${{ steps.version.outputs.version_type }})" - echo "version=${{ steps.version.outputs.version }}" >> $GITHUB_ENV + echo "cli-version=${{ steps.cli-version-changed.outputs.version }}" >> $GITHUB_ENV + echo "js-api-version=${{ steps.js-api-version-changed.outputs.version }}" >> $GITHUB_ENV + # Building jobs - build: + build-binaries: + needs: version + if: steps.cli-version-changed.outputs.changed == 'true' strategy: matrix: include: @@ -59,88 +102,24 @@ jobs: - os: macos-14 target: aarch64-apple-darwin code-target: darwin-arm64 + name: Package ${{ matrix.code-target }} runs-on: ${{ matrix.os }} - needs: retrieve-version - env: - version: ${{ needs.check.outputs.version }} - outputs: - version: ${{ env.version }} steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Install Node.js - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 - with: - node-version: 20 - - - name: Install toolchain - uses: moonrepo/setup-rust@e013866c4215f77c925f42f60257dec7dd18836e # v1.2.1 - with: - channel: stable - cache-target: release - cache-base: main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install arm64 toolchain - if: matrix.code-target == 'linux-arm64' || matrix.code-target == 'linux-arm64-musl' - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu - - - name: Install musl toolchain - if: matrix.code-target == 'linux-x64-musl' || matrix.code-target == 'linux-arm64-musl' - run: | - sudo apt-get update - sudo apt-get install -y musl-tools - - - name: Audit crates.io dependencies - if: matrix.code-target == 'linux-x64' - run: cargo audit - - - name: Set jemalloc page size for linux-arm64 - if: matrix.code-target == 'linux-arm64' - run: | - echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV - - # Build the CLI binary - - name: Build binaries - run: cargo build -p biome_cli --release --target ${{ matrix.target }} - env: - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-gnu-gcc - # Strip all debug symbols from the resulting binaries - RUSTFLAGS: "-C strip=symbols -C codegen-units=1" - # Inline the version of the npm package in the CLI binary - BIOME_VERSION: ${{ env.version }} - - # Copy the CLI binary and rename it to include the name of the target platform - - name: Copy CLI binary - if: matrix.os == 'windows-2022' - run: | - mkdir dist - cp target/${{ matrix.target }}/release/biome.exe ./dist/biome-${{ matrix.code-target }}.exe - - name: Copy CLI binary - if: matrix.os != 'windows-2022' - run: | - mkdir dist - cp target/${{ matrix.target }}/release/biome ./dist/biome-${{ matrix.code-target }} - - - - name: Upload Artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + - name: Run build + uses: ./.github/actions/build-cli with: - name: cli-${{ matrix.target }} - path: ./dist/biome-* - if-no-files-found: error + os: ${{ matrix.os }} + target: ${{ matrix.target }} + code-target: ${{ matrix.code-target }} + version: ${{ env.cli-version }} build-wasm: name: Build WASM runs-on: ubuntu-latest - needs: retrieve-version + needs: version + if: steps.cli-version-changed.outputs.changed == 'true' steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -165,11 +144,60 @@ jobs: ./packages/@biomejs/wasm-web if-no-files-found: error - release: + build-js-api: + name: Package JavaScript APIs + runs-on: ubuntu-latest + needs: version + if: steps.js-api-version-changed.outputs.changed == 'true' + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Install Node.js + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: 20 + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Cache pnpm modules + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + with: + path: ~/.pnpm-store + key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}- + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + + - name: Compile backends + run: | + pnpm --filter @biomejs/js-api run build:wasm-bundler + pnpm --filter @biomejs/js-api run build:wasm-node + pnpm --filter @biomejs/js-api run build:wasm-web + pnpm --filter @biomejs/backend-jsonrpc i + pnpm --filter @biomejs/backend-jsonrpc run build + + - name: Build package + run: | + pnpm --filter @biomejs/js-api i + pnpm --filter @biomejs/js-api run build + + - name: Upload JS API artifact + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: js-api + path: | + ./packages/@biomejs/js-api/dist + if-no-files-found: error + + # Publishing jobs + + publish-cli: name: Publish runs-on: ubuntu-latest needs: - - build + - build-binaries - build-wasm environment: npm-publish permissions: @@ -190,18 +218,29 @@ jobs: merge-multiple: true path: packages/@biomejs - - uses: knope-dev/action@407e9ef7c272d2dd53a4e71e39a7839e29933c48 # v2.1.0 + - name: Install Node.js + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: - version: 0.16.2 - github-token: ${{ secrets.GITHUB_TOKEN }} - - run: knope release + node-version: 20 + registry-url: 'https://registry.npmjs.org' + + - name: Generate npm packages + run: node packages/@biomejs/biome/scripts/generate-packages.mjs + + - name: Publish npm packages as latest + run: for package in packages/@biomejs/*; do if [ $package != "packages/@biomejs/js-api" ]; then npm publish $package --tag latest --access public --provenance; fi; done env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Upload assets to the latest tag + run: | + files=$(ls biome-* | tr '\n' ' ') + gh release upload @biomejs/biome@${{ env.cli-version }} $files - publish-npm: - name: Publish npm packages + publish-js-api: + name: Publish runs-on: ubuntu-latest - needs: release + needs: build-js-api environment: npm-publish permissions: contents: write @@ -209,47 +248,18 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Download CLI artifacts + - name: Download package artifact uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: - pattern: cli-* - merge-multiple: true - - name: Download WASM artifacts - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - pattern: wasm-* - merge-multiple: true - path: packages/@biomejs + name: js-api + path: packages/@biomejs/js-api/dist - name: Install Node.js uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version: 20 registry-url: 'https://registry.npmjs.org' - - - name: Publish npm packages as latest - run: for package in packages/@biomejs/*; do if [ $package != "packages/@biomejs/js-api" ]; then npm publish $package --tag latest --access public; fi; done + - name: Publish npm package as latest + run: npm publish packages/@biomejs/js-api --tag latest --access public --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Publish npm packages as nightly - run: for package in packages/@biomejs/*; do if [ $package != "packages/@biomejs/js-api" ]; then npm publish $package --tag nightly --access public; fi; done - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - publish-crate: - name: Publish crates - needs: release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Install toolchain - uses: moonrepo/setup-rust@e013866c4215f77c925f42f60257dec7dd18836e # v1.2.1 - with: - channel: stable - cache-target: release - cache-base: main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: katyo/publish-crates@v2 - with: - registry-token: ${{ secrets.CARGO_TOKEN }} diff --git a/.github/workflows/release_cli.yml b/.github/workflows/release_cli.yml index 192e425eed32..cb54afc16e3e 100644 --- a/.github/workflows/release_cli.yml +++ b/.github/workflows/release_cli.yml @@ -15,9 +15,6 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ env.version }} - prerelease: ${{ env.prerelease }} - nightly: ${{ env.nightly }} - version_changed: ${{ steps.version.outputs.changed }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -28,7 +25,6 @@ jobs: - name: Check version changes uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5 - if: env.nightly != 'true' id: version with: diff-search: true diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 0b30c11872c5..000000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,6585 +0,0 @@ -# Biome changelog - -This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Due to the nature of Biome as a toolchain, -it can be unclear what changes are considered major, minor, or patch. -Read our [guidelines to categorize a change](https://biomejs.dev/internals/versioning). - -New entries must be placed in a section entitled `Unreleased`. -Read -our [guidelines for writing a good changelog entry](https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#changelog). - -## Unreleased - -- Add `RegExpStringIterator` to TypeScript globals -- Fix [#4323](https://github.com/biomejs/biome/issues/4258), where `lint/a11y/useSemanticElement` accidentally showed recommendations for `role="searchbox"` instead of `role="search"` - -### Analyzer - -#### Bug fixes - -- Fix CSS parser case error, `@-moz-document url-prefix(https://example.com)` and `@-moz-document domain(example.com)` are now valid. Contributed by @eryue0220 -- Fix [#4258](https://github.com/biomejs/biome/issues/4258), where fixed css parse error with @-moz-document url-prefix(). Contributed by @eryue0220 - -### CLI - -#### Bug fixes - -- Don't parse the files that don't end with the json extension as JSON files in the `.vscode` directory ([#4391](https://github.com/biomejs/biome/issues/4391)). Contributed by @Conaclos - -- `biome migrate eslint` now correctly resolves scoped package named `eslint-config` with a path. - Contributed by @Conaclos - -- `biome migrate eslint` now correctly handles shared ESLint configuration that don't follow the ESLint naming convention ([#4528](https://github.com/biomejs/biome/issues/4528)). - - ESLint recommends that a package that exports a shared configuration be prefixed with `eslint-config-` or simply named `eslint-config`. - This is only a recommendation. - Packages that export shared configurations can have arbitrary names. - Biome is now able to load any package. - - Contributed by @Conaclos - -- `biome migrate eslint` now correctly handles ESLint configuration with `null` values in file lists ([#4740](https://github.com/biomejs/biome/issues/4740)). - Contributed by @Conaclos - -- Fix [#4202](https://github.com/biomejs/biome/issues/4202), where the formatting of the test function was different from prettier. Contributed by @mdm317 - -### Configuration - -### Editors - -### Formatter - -- Fix [#4413](https://github.com/biomejs/biome/issues/4413), where the GraphQL formatter adds a new line at the start of block comments on Windows. Contributed by @vohoanglong0107 - -### Bug fixes - -- Fix [#4121](https://github.com/biomejs/biome/issues/4326), don't ident a CSS selector when has leading comments. Contributed by @fireairforce - -- Fix [#4334](https://github.com/biomejs/biome/issues/4334), don't insert trailing comma on type import statement. Contributed by @fireairforce - -- Fix [#3229](https://github.com/biomejs/biome/issues/3229), where Biome wasn't idempotent when block comments were placed inside compound selectors. Contributed by @ematipico - -- Fix [#4026](https://github.com/biomejs/biome/issues/4026), don't move comments in `grid-template`. Contributed by @fireairforce - -- Fix [#4533](https://github.com/biomejs/biome/issues/4533), don't throw error when pseudeo class after a webkit scrollbar pseudeo element. - - The follow code will not report: - - ```css - ::-webkit-scrollbar-thumb:hover {} - ``` - - Contributed by @fireairforce - -- Fix [#4575](https://github.com/biomejs/biome/issues/4575), don't wrap selector identation after css comments. Contributed by @fireairforce - -- Fix [#4553](https://github.com/biomejs/biome/issues/4553), `noUselessFragments` fix result has invalid syntax for JSX attribute, the follow code will fix: - - ```jsx - Loading...}> - {children} - ; - ``` - - it will fix as: - - ```jsx - Loading...}> - {children} - ; - ``` - -- `noDuplicateProperties` now throws lint errors properly when we use `@supports` (fix [#4756](https://github.com/biomejs/biome/issues/4756)) Contributed by @mehm8128 - -- Fix [#4719](https://github.com/biomejs/biome/issues/4719), `bracketSameLine` now performs as expected when a comment is placed before the last JSX attribute. Contributed by @bushuai - -### JavaScript APIs - -### Linter - -#### New features - -- Add [noUselessUndefined](https://biomejs.dev/linter/rules/no-useless-undefined/). Contributed by @unvalley - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) accepts a new option `match` ([#4105](https://github.com/biomejs/biome/issues/4105)). - - You can now validate filenames with a regular expression. - For instance, you can allow filenames to start with `%`: - - ```json - { - "linter": { - "rules": { - "style": { - "useFilenamingConvention": { - "level": "warn", - "options": { - "match": "%?(.+?)[.](.+)", - "filenameCases": ["camelCase"] - } - } - } - } - } - } - ``` - - If the regular expression captures strings, the first capture is considered to be the name of the file, and the second one to be the extensions (dot-separated values). - The name of the file and the extensions are checked against `filenameCases`. - Given the previous configuration, the filename `%index.d.ts` is valid because the first capture `index` is in `camelCase` and the second capture `d.ts` include dot-separated values in `lowercase`. - On the other hand, `%Index.d.ts` is not valid because the first capture `Index` is in `PascalCase`. - - Note that specifying `match` disallows any exceptions that are handled by the rule by default. - For example, the previous configuration doesn't allow filenames to be prefixed with underscores, - a period or a plus sign. - You need to include them in the regular expression if you still want to allow these exceptions. - - Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) and [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention) `match` options now accept case-insensitive and case-sensitive groups. - - By default, the regular expression in `match` is case-sensitive. - You can now make it case-insensitive by using a case-insensitive group `(?i:)`. - For example, the regular expression `(?i:a)` matches `a` and `A`. - - Contributed by @Conaclos - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) now provides the `checkTypes` option ([#3998](https://github.com/biomejs/biome/issues/3998)). - - `noUndeclaredVariables` is inspired by the [no-undef ESLint rule](https://eslint.org/docs/latest/rules/no-undef). It reports all references that are not bound to any declarations within a module. - Node.js, JavaScript and TypeScript globals are ignored. - Biome provides the `javascript.globals` option to list additional globals that should be ignored by the rule. - - In TypeScript projects, developers often use global declaration files to declare global types. - Biome is currently unable to detect these global types. - This creates many false positives for `noUndeclaredVariables`. - - TypeScript is better suited to perform this kind of check. - As proof of this, TypeScript ESLint doesn't provide any rule that extends the `no-undef` ESLint rule. - - This is why we introduce today a new option `checkTypes` which, when it is set to `false`, ignores undeclared type references. - Given the following configuration... - - ```json - { - "linter": { - "rules": { - "correctness": { - "noUndeclaredVariables": { - "level": "error", - "options": { "checkTypes": false } - } - } - } - } - } - ``` - - ... `UndeclaredType` is not reported by the rule. - - ```ts - export default function(): UndeclaredType {} - ``` - - We plan to turn off the option by default in Biome 2.0 - Also, this will bring the Biome rule closer to the [no-undef ESLint rule](https://eslint.org/docs/latest/rules/no-undef). - - Contributed by @Conaclos - -- Add [noGlobalDirnameFilename](https://biomejs.dev/linter/rules/no-global-dirname-filename/). Contributed by @unvalley - -- [noForEach](https://biomejs.dev/linter/rules/no-for-each/) now provides a new option `validIdentifiers` ([#3351](https://github.com/biomejs/biome/issues/3351)) to specify which variable names are allowed to call `forEach`. - - Identifiers containing dots (e.g., "lib._") or empty strings are not allowed. Invalid configurations will produce a diagnostic warning. - - ```json - { - "linter": { - "rules": { - "complexity": { - "noForEach": { - "level": "error", - "options": { - "allowedIdentifiers": ["Effect", "_"] - } - } - } - } - } - } - ``` - - Contributed by @lucasweng - -- [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) now supports wildcards in the `function` option. - - ```json - { - "linter": { - "rules": { - "nursery": { - "useSortedClasses": { - "level": "warn", - "options": { - "functions": ["tw.*"] - } - } - } - } - } - } - ``` - - This allows the rule to handle class sorting for tagged template literals like `` tw.div`...` ``, used in libraries such as [twin.macro](https://github.com/ben-rogerson/twin.macro) and [react-twc](https://github.com/gregberge/twc). - -#### Enhancements - -- `useExportType` and `useImportType` now ignore TypeScript declaration files ([#4416](https://github.com/biomejs/biome/pull/4416)). Contributed by @Conaclos -- [useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/) now provides a code fix. - - ```diff - - const xs = new Array(); - + const xs = []; - ``` - - The code fix is currently marked as unsafe. - We plan to make it safe in a future release of Biome. - - Contributed by @Conaclos - -- `noUnusedImports` now reports empty named imports and suggests its removal ([#3574](https://github.com/biomejs/biome/issues/3574)). - - The rule now suggests the removal of empty named imports such as: - - ```diff - - import {} from "mod"; - ``` - - Contributed by @Conaclos - -- `noUnusedImports` now keeps comments separated from the import with a blank line ([#3401](https://github.com/biomejs/biome/issues/3401)). - - Here is an example: - - ```diff - // Orphan comment - - - // Header comment - - import {} from "mod"; - ``` - - Contributed by @Conaclos - -- `useValidTypeof` now accepts comparisons with variables. - - Previously, the rule required to compare a `typeof` expression against another `typeof` expression or a valid string literal. - We now accept more cases, notably comparison against a variable: - - ```js - if (typeof foo === bar) { - // ... - } - ``` - - Contributed by @Conaclos - -- [noUnknownProperty](https://biomejs.dev/linter/rules/no-unknown-property/) now accepts more known CSS properties ([#4549](https://github.com/biomejs/biome/issues/4549)). - - ```diff - - ['anchor-default', 'anchor-scroll', 'inset-area', 'position-animation', 'position-fallback', 'position-fallback-bounds', 'position-try-options'] - + ['anchor-scope', 'interpolate-size', 'line-fit-edge', 'masonry', 'masonry-auto-tracks', 'masonry-direction', 'masonry-fill', 'masonry-flow', 'masonry-slack', 'masonry-template-areas', 'masonry-template-tracks', 'position-anchor', 'position-area', 'position-try-fallbacks', 'position-visibility', 'scroll-start-target', 'text-box', 'view-transition-class', 'view-transition-group'] - ``` - - This change replaces deprecated properties, improving CSS validation. - - Contributed by @lucasweng - -#### Bug fixes - -- [noControlCharactersInRegex](https://biomejs.dev/linter/rules/no-control-characters-in-regex) no longer panics when it encounters an unterminated unicode escape sequence ([#4565](https://github.com/biomejs/biome/issues/4565)). Contributed by @Conaclos - -- [useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/) now reports all expressions using the `Array` constructors. - - Previously, the rule reported only use of the `Array` constructor in expressions statements. - - ```js - // This was reported - new Array(); - // This was not reported - const xs = new Array(); - ``` - - Contributed by @Conaclos - -- [useArrowFunction](https://biomejs.dev/linter/rules/use-arrow-function/) now preserves directives ([#4530](https://github.com/biomejs/biome/issues/4530)). - - Previously the rule removed the directives when a function expression was turned into an arrow function. - The rule now correctly keeps the directives. - - ```diff - - const withDirective = function () { - + const withDirective = () => { - "use server"; - return 0; - } - ``` - - Contributed by @Conaclos - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) is now able to bind read of value to a type-only import in ambient contexts ([#4526](https://github.com/biomejs/biome/issues/4526)). - - In the following code, `A` is now correctly bound to the type-only import. - Previously, `A` was reported as an undeclared variable. - - ```ts - import type { A } from "mod"; - - declare class B extends A {} - ``` - - Contributed by @Conaclos - -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports top-level variables in a global declaration file as unused. Contributed by @Conaclos - -- [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) no longer suggests renaming top-level variables in a global declaration file. Contributed by @Conaclos - -- [noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class/) no longer panics on malformed escape sequences that end with a multi-byte character ([#4587](https://github.com/biomejs/biome/issues/4587)). Contributed by @Conaclos - -- [noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports/) no longer reports used values imported as types in an external module ([#3895])(https://github.com/biomejs/biome/issues/3895). Contributed by @Conaclos - -- Fixed a panic related to bogus import statements in `useExhaustiveDependencies` ([#4568](https://github.com/biomejs/biome/issues/4568)) Contributed by @dyc3 - -- Fixed `useSortedClasses` false positive and Supplementary test case ([#3394](https://github.com/biomejs/biome/issues/3394)) Contributed by @hangaoke1 -- [noLabelWithoutControl](https://biomejs.dev/linter/rules/no-label-without-control/) detects button tags as input ([#4511])(https://github.com/biomejs/biome/issues/4511). Contributed by @unvalley - -- [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) now handles `JsxAttributeInitializerClause`, ensuring that fragments inside expressions like ` />` are preserved. ([#4208](https://github.com/biomejs/biome/issues/4208)). Contributed by @MaxtuneLee - -- [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) now suggests code fixes that match the JSX quote style of the formatter ([#4855](https://github.com/biomejs/biome/issues/4855)). Contributed by @lucasweng - -### Parser - -#### Bug fixes - -- Fix [#4317](https://github.com/biomejs/biome/issues/4317), setter parameter can contain a trailing comma, the following example will now parsed correctly: - - ```js - export class DummyClass { - set input( - value: string, - ) {} - } - ``` - - Contributed by @fireairforce - -- Fix [#3836](https://github.com/biomejs/biome/issues/3836), css parser allow multiple semicolons after a declaration, the following example will now parsed correctly: - - ```css - .foo { - color: red;; - } - ``` - - Contributed by @fireairforce - -- Fix [#342](https://github.com/biomejs/biome/issues/342), js parser handle unterminated `JSX_STRING_LITERAL` properly - - ```jsx - function Comp() { - return ( - {line || <> } - ) - } - ``` - - Contributed by @fireairforce - -- [noUnusedFunctionParameters](https://biomejs.dev/linter/rules/no-unused-function-parameters/) and [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports a parameter as unused when another parameter has a constructor type with the same parameter name ([#4227](https://github.com/biomejs/biome/issues/4227)). - - In the following code, the `name` parameter is no longer reported as unused. - - ```ts - export class Foo { - bar(name: string, _class: new (name: string) => any) { - return name - } - } - ``` - - Contributed by @Conaclos - -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) now accepts dependency names with dots. Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now correctly handles renamed exports ([#4254](https://github.com/biomejs/biome/issues/4254)). - - The rule allows the filename to be named as one of the exports of the module. - For instance, the file containing the following export can be named `Button`. - - ```js - class Button {} - export { Button } - ``` - - The rule now correctly handles the renaming of an export. - For example, the file containing the following export can only be named `Button`. - Previously the rule expected the file to be named `A`. - - ```js - class A {} - export { A as Button } - ``` - - Contributed by @Conaclos - -- [useConsistentMemberAccessibility](https://biomejs.dev/linter/rules/use-consistent-member-accessibility/) now ignore private class members such as `#property` ([#4276](https://github.com/biomejs/biome/issues/4276)). Contributed by @Conaclos - -- [noUnknownFunction](https://biomejs.dev/linter/rules/no-unknown-function/) correctly handles `calc-size` function ([#4212](https://github.com/biomejs/biome/issues/4212)). - - The following code `calc-size` is no longer reported as unknown: - - ```css - .a { height: calc-size(0px); } - ``` - - Contributed by @fireairforce - - - [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now allows configuring conventions for readonly index signatures. - - Contributed by @sepruko - -- [noDuplicateCustomProperties](https://biomejs.dev/linter/rules/no-duplicate-custom-properties/) now correctly handles custom properties and ignores non-custom properties. - Previously, the rule incorrectly reported duplicates for all properties, including non-custom ones. Contributed by @togami2864 - -### Parser - -#### Bug Fixes - -- The CSS parser now accepts more emoji in identifiers ([#3627](https://github.com/biomejs/biome/issues/3627#issuecomment-2392388022)). - - Browsers accept more emoji than the standard allows. - Biome now accepts these additional emojis. - - The following code is now correctly parsed: - - ```css - p { - --✨-color: red; - color: var(--✨-color); - } - ``` - - Contributed by @Conaclos - -- Add support for parsing typescript's `resolution-mode` in Import Types([#2115](https://github.com/biomejs/biome/issues/2115)) - - ```ts - export type Fs = typeof import('fs', { with: { 'resolution-mode': 'import' } }); - export type TypeFromRequire = - import("pkg", { with: { "resolution-mode": "require" } }).TypeFromRequire; - export type TypeFromImport = - import("pkg", { with: { "resolution-mode": "import" } }).TypeFromImport; - ``` - - Contributed by @fireairforce - -## v1.9.3 (2024-10-01) - -### CLI - -#### New features - -- GritQL queries that match functions or methods will now match async functions or methods as well. - - If this is not what you want, you can capture the `async` keyword (or its absence) in a metavariable and assert its emptiness: - - ```grit - $async function foo() {} where $async <: . - ``` - - Contributed by @arendjr - -#### Bug fixes - -- Fix [#4077](https://github.com/biomejs/biome/issues/4077): Grit queries no longer need to match the statement's trailing semicolon. Contributed by @arendjr - -- Fix [#4102](https://github.com/biomejs/biome/issues/4102). Now the CLI command `lint` doesn't exit with an error code when using `--write`/`--fix`. Contributed by @ematipico - -### Configuration - -#### Bug fixes -- Fix [#4125](https://github.com/biomejs/biome/issues/4125), where `noLabelWithoutControl` options where incorrectly marked as mandatory. Contributed by @ematipico - -### Editors - -- Fix a case where CSS files weren't correctly linted using the default configuration. Contributed by @ematipico - -#### Bug fixes - -- Fix [#4116](https://github.com/biomejs/biome/issues/4116). Unify LSP code action kinds. Contributed by @vitallium - -### Formatter - -#### Bug fixes - -- Fix [#3924](https://github.com/biomejs/biome/issues/3924) where GraphQL formatter panics in block comments with empty line. Contributed by @vohoanglong0107 -- Fix [#3364](https://github.com/biomejs/biome/issues/3364) where the `useSelfClosingElements` rule forces the `script` tag to be self-closing. Previously, this rule applies to all elements and cannot be disabled for native HTML elements. - - Now, this rule accepts a `ignoreHtmlElements` option, which when set to `true`, ignores native HTML elements and allows them to be non-self-closing. - - Contributed by @abidjappie - -- Fix a case where raw values inside `url()` functions weren't properly trimmed. - ```diff - .value { - - background: url( - - whitespace-around-string - - ); - + background: url(whitespace-around-string); - } - ``` - Contributed by @ematipico - -- Fixed [#4076](https://github.com/biomejs/biome/issues/4076), where a media query wasn't correctly formatted: - ```diff - .class { - - @media (1024px <= width <=1280px) { - + @media (1024px <= width <= 1280px) { - color: red; - } - } - ``` - Contributed by @blaze-d83 - -### JavaScript API - -#### Bug fixes - -- Fix [#3881](https://github.com/biomejs/biome/issues/3881), by updating the APIs to use the latest WASM changes. Contributed by @ematipico - -### Linter - -#### New features - -- Add [noDescendingSpecificity](https://biomejs.dev/linter/rules/no-descending-specificity/). Contributed by @tunamaguro - -- Add [noNestedTernary](https://biomejs.dev/linter/rules/no-nested-ternary/). Contributed by @kaykdm - -- Add [noTemplateCurlyInString](https://biomejs.dev/linter/rules/no-template-curly-in-string/). Contributed by @fireairforce - -- Add [noOctalEscape](https://biomejs.dev/linter/rules/no-octal-escape/). Contributed by @fireairforce - -#### Enhancements - -- Add an option `reportUnnecessaryDependencies` to [useExhaustiveDependencies](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/). - - Defaults to true. When set to false, errors will be suppressed for React hooks that declare dependencies but do not use them. - - Contributed by @simon-paris - -- Add an option `reportMissingDependenciesArray` to [useExhaustiveDependencies](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/). Contributed by @simon-paris - -#### Bug fixes - -- [noControlCharactersInRegex](https://www.biomejs.dev/linter/rules/no-control-characters-in-regex) no longer panics on regexes with incomplete escape sequences. Contributed by @Conaclos - -- [noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class/) no longer reports issues outside of character classes. - - The following code is no longer reported: - - ```js - /[a-z]👍/; - ``` - - Contributed by @Conaclos - -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) no longer reports Node.js builtin modules as undeclared dependencies. - - The rule no longer reports the following code: - - ```js - import * as fs from "fs"; - ``` - - Contributed by @Conaclos - -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer panics when suggesting the renaming of a variable at the start of a file ([#4114](https://github.com/biomejs/biome/issues/4114)). Contributed by @Conaclos - -- [noUselessEscapeInRegex](https://biomejs.dev/linter/rules/no-useless-escape-in-regex/) no longer panics on regexes that start with an empty character class. Contributed by @Conaclos - -- [noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/) no longer panics when it encounters malformed code. Contributed by @Conaclos - -- [noUnusedFunctionParameters](https://biomejs.dev/linter/rules/no-unused-function-parameters/) no longer reports unused parameters inside an object pattern with a rest parameter. - - In the following code, the rule no longer reports `a` as unused. - - ```js - function f({ a, ...rest }) { - return rest; - } - ``` - - This matches the behavior of [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/). - - Contributed by @Conaclos - -- [useButtonType](https://biomejs.dev/linter/rules/use-button-type/) no longer reports dynamically created button with a valid type ([#4072](https://github.com/biomejs/biome/issues/4072)). - - The following code is no longer reported: - - ```js - React.createElement("button", { type: "button" }, "foo") - ``` - - Contributed by @Conaclos - -- [useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) now ignores elements with the `img` role ([#3994](https://github.com/biomejs/biome/issues/3994)). - - [MDN recommends](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/img_role) using `role="img"` for grouping images or creating an image from other elements. - The following code is no longer reported: - - ```jsx -
-

🐈 😂

-
- ``` - - Contributed by @Conaclos - -- [useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) now ignores `alert` and `alertdialog` roles ([#3858](https://github.com/biomejs/biome/issues/3858)). Contributed by @Conaclos - -- [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) don't create invaild JSX code when Fragments children contains JSX Expression and in a LogicalExpression. Contributed by @fireairforce - -### Parser - -#### Bug fixes - -- Forbid undefined as type name for typescript parser. Contributed by @fireairforce - -## v1.9.2 (2024-09-19) - -### CLI - -#### New features - -- Added support for custom GritQL definitions, including: - - Pattern and predicate definitions: https://docs.grit.io/guides/patterns - - Function definitions: https://docs.grit.io/language/functions#function-definitions - - Contributed by @arendjr - -#### Bug fixes - -- Fix [#3917](https://github.com/biomejs/biome/issues/3917), where the fixed files were incorrectly computed. Contributed by @ematipico -- Fixed an issue that caused GritQL `contains` queries to report false positives when the matched - node appeared inside a sibling node. Contributed by @arendjr - -### Editors - -#### Bug fixes - -- Fix [#3923](https://github.com/biomejs/biome/issues/3923). Now the `.editorconfig` is correctly parsed by the LSP, and the options are correctly applied to files when formatting is triggered. - Plus, the Biome LSP now watches for any change to the `.editorconfig`, and updates the formatting settings. -- Reduced the number of log files generated by the LSP server. Now the maximum number of logs saved on disk is **seven**. Contributed by @ematipico -- Fix the code actions capabilities available in the LSP Biome server. Before, the LSP was using the default capabilities, which resulted in pulling code actions even when they were disabled by the editor. - - This means that the code actions are pulled by the client **only** when the editor enables `quickfix.biome`, `source.organizeImports.biome` and `source.fixAll.biome`. - - Now, if you enable `organizeImports.enabled: true` in the `biome.json`, and then you configure your editor with the following code action `source.organizeImports.biome: false`, the editor **won't** sort the imports. - - Contributed by @ematipico - -### Linter - -#### New features - -- Add [nursery/noMissingVarFunction](https://biomejs.dev/linter/rules/no-missing-var-function). Contributed by @michellocana -- Add [nursery/useComponentExportOnlyModules](https://biomejs.dev/linter/rules/use-component-export-only-modules). Use this rule in React projects to enforce a code styling that fits React Refresh. Contributed by @GunseiKPaseri - -#### Bug fixes - -- [noLabelWithoutControl](https://biomejs.dev/linter/rules/no-label-without-control/) now accept JSX expression as label value ([#3875](https://github.com/biomejs/biome/issues/3875)). Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) no longer suggests names with a disallowed case ([#3952](https://github.com/biomejs/biome/issues/3952)). Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now recognizes file names starting with ASCII digits as lowercase ([#3952](https://github.com/biomejs/biome/issues/3952)). - - Thus, `2024-09-17-filename`, `2024_09_17_filename` and `20240917FileName` are in `kebab-case`, `snake_case`, and `camelCase` respectively. - - Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now applies the configured formats to the file extensions ([#3650](https://github.com/biomejs/biome/discussions/3650)). Contributed by @Conaclos - -### Parser - -#### Bug fixes - -- [useStrictMode](https://biomejs.dev/linter/rules/use-strict-mode/) now reports Script files with some directives, but without the `use strict` directive. Contributed by @Conaclos - -- The CSS parser now accepts the characters U+FFDCF and U+FFFD in identifiers. Contributed by @Conaclos - -## v1.9.1 (2024-09-15) - -### CLI - -#### Bug fixes - -- `useEditorConfig` now loads the editorconfig when running `biome ci` [#3864](https://github.com/biomejs/biome/issues/3864). Contributed by @dyc3 - -### Editors - -#### Bug fixes - -- Revert [#3731](https://github.com/biomejs/biome/pull/3731) to fix broken quick fixes and code actions. Contributed by @nhedger - -### Linter - -#### New Features - -- Add [nursery/noProcessEnv](https://biomejs.dev/linter/rules/no-process-env/). Contributed by @unvalley - -#### Bug fixes - -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) now ignores `@/` imports and recognizes type imports from Definitely Typed and `bun` imports. Contributed by @Conaclos - -## v1.9.0 (2024-09-12) - -### Analyzer - -- Implement the [semantic model for CSS](https://github.com/biomejs/biome/pull/3546). Contributed by @togami2864 - -### CLI - -#### New features - -- Add `--graphql-linter-enabled` option, to control whether the linter should be enabled or not for GraphQL files. Contributed by @ematipico - -- New EXPERIMENTAL `search` command. The search command allows you to search a Biome project using [GritQL syntax](https://biomejs.dev/reference/gritql). - - GritQL is a powerful language that lets you do _structural_ searches on your codebase. This means that trivia such as whitespace or even the type of strings quotes used will be ignored in your search query. It also has many features for querying the structure of your code, making it much more elegant for searching code than regular expressions. - - While we believe this command may already be useful to users in some situations (especially when integrated in the IDE extensions!), we also had an ulterior motive for adding this command: We intend to utilize GritQL for our plugin efforts, and by allowing our users to try it out in a first iteration, we hope to gain insight in the type of queries you want to do, as well as the bugs we need to focus on. - - For now, the `search` command is explicitly marked as EXPERIMENTAL, since many bugs remain. Keep this in mind when you try it out, and please [let us know](https://github.com/biomejs/biome/issues) your issues! - - Note: GritQL escapes code snippets using backticks, but most shells interpret backticks as command invocations. To avoid this, it's best to put _single quotes_ around your Grit queries. - - ```shell - biome search '`console.log($message)`' # find all `console.log` invocations - ``` - - Contributed by @arendjr and @BackupMiles - -- The option `--max-diagnostics` now accept a `none` value, which lifts the limit of diagnostics shown. Contributed by @ematipico - - Add a new reporter `--reporter=gitlab`, that emits diagnostics for using the [GitLab Code Quality report](https://docs.gitlab.com/ee/ci/testing/code_quality.html#implement-a-custom-tool). - - ```json - [ - { - "description": "Use === instead of ==. == is only allowed when comparing against `null`", - "check_name": "lint/suspicious/noDoubleEquals", - "fingerprint": "6143155163249580709", - "severity": "critical", - "location": { - "path": "main.ts", - "lines": { - "begin": 4 - } - } - } - ] - ``` - - Contributed by @NiclasvanEyk - -- Add new options to the `lsp-proxy` and `start` commands: - - `--log-path`: a directory where to store the daemon logs. The commands also accepts the environment variable `BIOME_LOG_PATH`. - - `--log-prefix-name`: a prefix that's added to the file name of the logs. It defaults to `server.log`. The commands also accepts the environment variable `BIOME_LOG_PREFIX_NAME`. - - @Contributed by @ematipico - -#### Enhancements - -- When a `--reporter` is provided, and it's different from the default one, the value provided by via `--max-diagnostics` is ignored and **the limit is lifted**. Contributed by @ematipico - -- `biome init` now generates a new config file with more options set. - This change intends to improve discoverability of the options and to set the more commonly used options to their default values. - Contributed by @Conaclos - -- The `--verbose` flag now reports the list of files that were evaluated, and the list of files that were fixed. - The **evaluated** files are the those files that can be handled by Biome, files that are ignored, don't have an extension or have an extension that Biome can't evaluate are excluded by this list. - The **fixed** files are those files that were handled by Biome and *changed*. Files that stays the same after the process are excluded from this list. - - ```shell - VERBOSE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ℹ Files processed: - - - biome/biome.json - - biome/packages/@biomejs/cli-win32-arm64/package.json - - biome/packages/tailwindcss-config-analyzer/package.json - - VERBOSE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ℹ Files fixed: - - - biome/biome/packages/tailwindcss-config-analyzer/src/generate-tailwind-preset.ts - ``` - - Contributed by @ematipico - -- Allow passing `nursery` to the `--only` and `--skip` filters. - - The `--only` option allows you to run a given rule or rule group. - The `--skip` option allows you to skip the execution of a given group or a given rule. - - Previously, it was not possible to pass `nursery`. - This restriction is now removed, as it may make sense to skip the nursery rules that a project has enabled. - - Contributed by @Conaclos - -- The CLI now returns an error code when calling a command in `stdin` mode, and the contents of the files aren't fixed. For example, the following example will result in an error code of `1` because the `lint` command triggers some lint rules: - - ```shell - echo "let x = 1" | biome lint --stdin-file-path=stdin.js - ``` - - Contributed by @ematipico - -#### Bug fixes - -- `biome lint --write` now takes `--only` and `--skip` into account ([#3470](https://github.com/biomejs/biome/issues/3470)). Contributed by @Conaclos - -- Fix [#3368](https://github.com/biomejs/biome/issues/3368), now the reporter `github` tracks the diagnostics that belong to formatting and organize imports. Contributed by @ematipico - -- Fix [#3545](https://github.com/biomejs/biome/issues/3545), display a warning, 'Avoid using unnecessary Fragment,' when a Fragment contains only one child element that is placed on a new line. Contributed by @satojin219 - -- Migrating from Prettier or ESLint no longer overwrite the `overrides` field from the configuration ([#3544](https://github.com/biomejs/biome/issues/3544)). Contributed by @Conaclos - -- Fix JSX expressions for `noAriaHiddenOnFocusable` ([#3708](https://github.com/biomejs/biome/pull/3708)). Contributed by @anthonyshew - -- Fix edge case for `` elements that use `role="img"` ([#3728](https://github.com/biomejs/biome/pull/3728)). Contributed by @anthonyshew - -- Fix [#3633](https://github.com/biomejs/biome/issues/3633), where diagnostics where incorrectly printed if the code has errors. Contributed by @ematipico - -- Allow `aria-label` on heading to prevent `useHeadingContent` diagnostic ([#3767](https://github.com/biomejs/biome/pull/3767)). Contributed by @anthonyshew - -- Fix edge case [#3791](https://github.com/biomejs/biome/issues/3791) for rule `noFocusedTests` being used with non-string-like expressions ([#3793](https://github.com/biomejs/biome/pull/3793)). Contributed by @h-a-n-a - -- Fix optional ARIA properties for `role="separator"` in `useAriaPropsForRole` ([#3856](https://github.com/biomejs/biome/pull/3856)). Contributed by @anthonyshew - -### Configuration - -- Add support for loading configuration from `.editorconfig` files ([#1724](https://github.com/biomejs/biome/issues/1724)). - - Configuration supplied in `.editorconfig` will be overridden by the configuration in `biome.json`. Support is disabled by default and can be enabled by adding the following to your formatter configuration in `biome.json`: - - ```json - { - "formatter": { - "useEditorconfig": true - } - } - ``` - - Contributed by @dyc3 - -- `overrides` from an extended configuration is now merged with the `overrides` of the extension. - - Given the following shared configuration `biome.shared.json`: - - ```json5 - { - "overrides": [ - { - "include": ["**/*.json"], - // ... - } - ] - } - ``` - - and the following configuration: - - ```json5 - { - "extends": ["./biome.shared.json"], - "overrides": [ - { - "include": ["**/*.ts"], - // ... - } - ] - } - ``` - - Previously, the `overrides` from `biome.shared.json` was overwritten. - It is now merged and results in the following configuration: - - ```json5 - { - "extends": ["./biome.shared.json"], - "overrides": [ - { - "include": ["**/*.json"], - // ... - }, - { - "include": ["**/*.ts"], - // ... - } - ] - } - ``` - - Contributed by @Conaclos - -### Editors - -- Fix [#3577](https://github.com/biomejs/biome/issues/3577), where the update of the configuration file was resulting in the creation of a new internal project. Contributed by @ematipico - -- Fix [#3696](https://github.com/biomejs/biome/issues/3696), where `biome.jsonc` was incorrectly parsed with incorrect options. Contributed by @ematipico - -### Formatter - -- The CSS formatter is enabled by default. Which means that you don't need to opt-in anymore using the configuration file `biome.json`: - - ```diff - { - - "css": { - - "formatter": { - - "enabled": true - - } - - } - } - ``` - - Contributed by @ematipico - -- Add parentheses for nullcoalescing in ternaries. - - This change aligns on [Prettier 3.3.3](https://github.com/prettier/prettier/blob/main/CHANGELOG.md#333). - This adds clarity to operator precedence. - - ```diff - - foo ? bar ?? foo : baz; - + foo ? (bar ?? foo) : baz; - ``` - - Contributed by @Conaclos - -- Keep the parentheses around `infer ... extends` declarations in type unions and type intersections ([#3419](https://github.com/biomejs/biome/issues/3419)). Contributed by @Conaclos - -- Keep parentheses around a `yield` expression inside a type assertion. - - Previously, Biome removed parentheses around some expressions that require them inside a type assertion. - For example, in the following code, Biome now preserves the parentheses. - - ```ts - function* f() { - return (yield 0); - } - ``` - - Contributed by @Conaclos - -- Remove parentheses around expressions that don't need them inside a decorator. - - Biome now matches Prettier in the following cases: - - ```diff - class { - - @(decorator) - + @decorator - method() {} - }, - class { - - @(decorator()) - + @decorator() - method() {} - }, - class { - @(decorator?.()) - method() {} - }, - ``` - - Contributed by @Conaclos - -- Keep parentheses around objects preceded with a `@satisfies` comment. - - In the following example, parentheses are no longer removed. - - ```ts - export const PROPS = /** @satisfies {Record} */ ({ - prop: 0, - }); - ``` - - Contributed by @Conaclos - -### Linter - -#### Promoted rules - -New rules are incubated in the nursery group. -Once stable, we promote them to a stable group. - -The following CSS rules are promoted: - -- [a11y/useGenericFontNames](https://biomejs.dev/linter/rules/use-generic-font-names/) -- [correctness/noInvalidDirectionInLinearGradient](https://biomejs.dev/linter/rules/no-invalid-direction-in-linear-gradient/) -- [correctness/noInvalidGridAreas](https://biomejs.dev/linter/rules/no-invalid-grid-areas/) -- [correctness/noInvalidPositionAtImportRule](https://biomejs.dev/linter/rules/no-invalid-position-at-import-rule/) -- [correctness/noUnknownFunction](https://biomejs.dev/linter/rules/no-unknown-function/) -- [correctness/noUnknownMediaFeatureName](https://biomejs.dev/linter/rules/no-unknown-media-feature-name/) -- [correctness/noUnknownProperty](https://biomejs.dev/linter/rules/no-unknown-property/) -- [correctness/noUnknownUnit](https://biomejs.dev/linter/rules/no-unknown-unit/) -- [correctness/noUnmatchableAnbSelector](https://biomejs.dev/linter/rules/no-unmatchable-anb-selector/) -- [suspicious/noDuplicateAtImportRules](https://biomejs.dev/linter/rules/no-duplicate-at-import-rules/) -- [suspicious/noDuplicateFontNames](https://biomejs.dev/linter/rules/no-duplicate-font-names/) -- [suspicious/noDuplicateSelectorsKeyframeBlock](https://biomejs.dev/linter/rules/no-duplicate-selectors-keyframe-block/) -- [suspicious/noEmptyBlock](https://biomejs.dev/linter/rules/no-empty-block/) -- [suspicious/noImportantInKeyframe](https://biomejs.dev/linter/rules/no-important-in-keyframe/) -- [suspicious/noShorthandPropertyOverrides](https://biomejs.dev/linter/rules/no-shorthand-property-overrides/) - -The following JavaScript rules are promoted: - -- [a11y/noLabelWithoutControl](https://biomejs.dev/linter/rules/no-label-without-control/) -- [a11y/useFocusableInteractive](https://biomejs.dev/linter/rules/use-focusable-interactive/) -- [a11y/useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) -- [complexity/noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/) -- [complexity/noUselessUndefinedInitialization](https://biomejs.dev/linter/rules/no-useless-undefined-initialization/) -- [complexity/useDateNow](https://biomejs.dev/linter/rules/use-date-now/) -- [correctness/noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) -- [correctness/noInvalidBuiltinInstantiation](https://biomejs.dev/linter/rules/no-invalid-builtin-instantiation/) -- [correctness/noUnusedFunctionParameters](https://biomejs.dev/linter/rules/no-unused-function-parameters/) -- [correctness/useImportExtensions](https://biomejs.dev/linter/rules/use-import-extensions/) -- [performance/useTopLevelRegex](https://biomejs.dev/linter/rules/use-top-level-regex/) -- [style/noDoneCallback](https://biomejs.dev/linter/rules/no-done-callback/) -- [style/noYodaExpression](https://biomejs.dev/linter/rules/no-yoda-expression/) -- [style/useConsistentBuiltinInstantiation](https://biomejs.dev/linter/rules/use-consistent-builtin-instantiation/) -- [style/useDefaultSwitchClause](https://biomejs.dev/linter/rules/use-default-switch-clause/) -- [style/useExplicitLengthCheck](https://biomejs.dev/linter/rules/use-explicit-length-check/) -- [style/useThrowNewError](https://biomejs.dev/linter/rules/use-throw-new-error/) -- [style/useThrowOnlyError](https://biomejs.dev/linter/rules/use-throw-only-error/) -- [suspicious/noConsole](https://biomejs.dev/linter/rules/no-console/) -- [suspicious/noEvolvingTypes](https://biomejs.dev/linter/rules/no-evolving-types/) -- [suspicious/noMisplacedAssertion](https://biomejs.dev/linter/rules/no-misplaced-assertion/) -- [suspicious/noReactSpecificProps](https://biomejs.dev/linter/rules/no-react-specific-props/) -- [suspicious/useErrorMessage](https://biomejs.dev/linter/rules/use-error-message/) -- [suspicious/useNumberToFixedDigitsArgument](https://biomejs.dev/linter/rules/use-number-to-fixed-digits-argument/) - -#### Deprecated rules - -- `correctness/noInvalidNewBuiltin` is deprecated. Use [correctness/noInvalidBuiltinInstantiation](https://biomejs.dev/linter/rules/no-invalid-builtin-instantiation/) instead. -- `style/useSingleCaseStatement` is deprecated. Use [correctness/noSwitchDeclarations](https://biomejs.dev/linter/rules/no-switch-declarations/) instead. -- `suspicious/noConsoleLog` is deprecated. Use [suspicious/noConsole](https://biomejs.dev/linter/rules/no-console/) instead. - -#### New features - -- Implement [css suppression action](https://github.com/biomejs/biome/issues/3278). Contributed by @togami2864 - -- Add support for GraphQL linting. Contributed by @ematipico - -- Add [nursery/noCommonJs](https://biomejs.dev/linter/rules/no-common-js/). Contributed by @minht11 - -- Add [nursery/noDuplicateCustomProperties](https://biomejs.dev/linter/rules/no-duplicate-custom-properties/). Contributed by @chansuke - -- Add [nursery/noEnum](https://biomejs.dev/linter/rules/no-enum/). Contributed by @nickfla1 - -- Add [nursery/noDynamicNamespaceImportAccess](https://biomejs.dev/linter/no-dynamic-namespace-import-access/). Contributed by @minht11 - -- Add [nursery/noIrregularWhitespace](https://biomejs.dev/linter/rules/no-irregular-whitespace). Contributed by @michellocana - -- Add [nursery/noRestrictedTypes](https://biomejs.dev/linter/no-restricted-types/). Contributed by @minht11 - -- Add [nursery/noSecrets](https://biomejs.dev/linter/rules/no-secrets/). Contributed by @SaadBazaz - -- Add [nursery/noUselessEscapeInRegex](https://biomejs.dev/linter/rules/no-useless-escape-in-regex/). Contributed by @Conaclos - -- Add [nursery/noValueAtRule](https://biomejs.dev/linter/rules/no-value-at-rule/). Contributed by @rishabh3112 - -- Add [nursery/useAriaPropsSupportedByRole](https://biomejs.dev/linter/rules/use-aria-props-supported-by-role/). Contributed by @ryo-ebata - -- Add [nursery/useConsistentMemberAccessibility](https://biomejs.dev/linter/rules/use-consistent-member-accessibility/). Contributed by @seitarof - -- Add [nursery/useStrictMode](https://biomejs.dev/linter/rules/use-strict-mode/). Contributed by @ematipico - -- Add [nursery/useTrimStartEnd](https://biomejs.dev/linter/rules/use-trim-start-end/). Contributed by @chansuke - -- Add [nursery/noIrregularWhitespace](https://biomejs.dev/linter/rules/no-irreguluar-whitespace/). Contributed by @DerTimonius - -#### Enhancements - -- Rename `nursery/noUnknownSelectorPseudoElement` to `nursery/noUnknownPseudoElement`. Contributed by @togami2864 - -- The CSS linter is now enabled by default. Which means that you don't need to opt-in anymore using the configuration file `biome.json`: - - ```diff - { - - "css": { - - "linter": { - - "enabled": true - - } - - } - } - ``` - - Contributed by @ematipico - -- The JavaScript linter recognizes TypeScript 5.5 and 5.6 globals. Contributed by @Conaclos - -- [noBlankTarget](https://biomejs.dev/linter/rules/no-blank-target/) now supports an array of allowed domains. - - The following configuration allows `example.com` and `example.org` as blank targets. - - ```json - "linter": { - "rules": { - "a11y": { - "noBlankTarget": { - "level": "error", - "options": { - "allowDomains": ["example.com", "example.org"] - } - } - } - } - } - ``` - - Contributed by @Jayllyz - -- [noConsole](https://biomejs.dev/linter/rules/no-console/) now accepts an option that specifies some allowed calls on `console`. Contributed by @Conaclos - -- Add an `ignoreNull` option for [noDoubleEquals](https://biomejs.dev/linter/rules/no-double-equals/). - - By default the rule allows loose comparisons against `null`. - The option `ignoreNull` can be set to `false` for reporting loose comparison against `null`. - - Contributed by @peaBerberian. - -- [noDuplicateObjectKeys](https://biomejs.dev/linter/rules/no-duplicate-object-keys/) now works for JSON and JSONC files. Contributed by @ematipico - -- [noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) now reports direct use of an enum member before its declaration. - - In the following code, `A` is reported as use before its declaration. - - ```ts - enum E { - B = A << 1, - A = 1, - } - ``` - - Contributed by @Conaclos - -- [noNodejsModules](https://biomejs.dev/linter/rules/no-nodejs-modules/) now ignores imports of a package which has the same name as a Node.js module. Contributed by @Conaclos - -- [noNodejsModules](https://biomejs.dev/linter/rules/no-nodejs-modules/) now ignores type-only imports ([#1674](https://github.com/biomejs/biome/issues/1674)). - - The rule no longer reports type-only imports such as: - - ```ts - import type assert from "assert"; - import type * as assert2 from "assert"; - ``` - - Contributed by @Conaclos - -- [noRedundantUseStrict](https://biomejs.dev/linter/rules/no-redundant-use-strict/) no longer reports `"use strict"` directives when the `package.json` marks explicitly the file as a script using the field `"type": "commonjs"`. Contributed by @ematipico - -- [noStaticOnlyClass](https://biomejs.dev/linter/rules/no-static-only-class/) no longer reports a class that extends another class ([#3612](https://github.com/biomejs/biome/issues/3612)). Contributed by @errmayank - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) no longer reports a direct reference to an enum member ([#2974](https://github.com/biomejs/biome/issues/2974)). - - In the following code, the `A` reference is no longer reported as an undeclared variable. - - ```ts - enum E { - A = 1, - B = A << 1, - } - ``` - - Contributed by @Conaclos - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) recognized Svelte 5 runes in Svelte components and svelte files. - - Svelte 5 introduced runes. - The rule now recognizes Svelte 5 runes in files ending with the `.svelte`, `.svelte.js` or `.svelte.ts` extensions. - - Contributed by @Conaclos - -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) now checks TypeScript declaration files. - - This allows to report a type that is unused because it isn't exported. - Global declarations files (declarations files without exports and imports) are still ignored. - - Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now supports [unicase](https://en.wikipedia.org/wiki/Unicase) letters. - - [unicase](https://en.wikipedia.org/wiki/Unicase) letters have a single case: they are neither uppercase nor lowercase. - Biome now accepts filenames in unicase. - For example, the filename `안녕하세요` is now accepted. - - We still reject a name that mixes unicase characters with lowercase or uppercase characters. - For example, the filename `A안녕하세요` is rejected. - - This change also fixes [#3353](https://github.com/biomejs/biome/issues/3353). - Filenames consisting only of numbers are now accepted. - - Contributed by @Conaclos - -- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now supports Next.js/Nuxt/Astro dynamic routes ([#3465](https://github.com/biomejs/biome/issues/3465)). - - [Next.js](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments), [SolidStart](https://docs.solidjs.com/solid-start/building-your-application/routing#renaming-index), [Nuxt](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route), and [Astro](https://docs.astro.build/en/guides/routing/#rest-parameters) support dynamic routes such as `[...slug].js` and `[[...slug]].js`. - - Biome now recognizes this syntax. `slug` must contain only alphanumeric characters. - - Contributed by @Conaclos - -- [useExportType](https://biomejs.dev/linter/rules/use-export-type/) no longer reports empty `export` ([#3535](https://github.com/biomejs/biome/issues/3535)). - - An empty `export {}` allows you to force TypeScript to consider a file with no imports and exports as an EcmaScript module. - While `export type {}` is valid, it is more common to use `export {}`. - Users may find it confusing that the linter asks them to convert it to `export type {}`. - Also, a bundler should be able to remove `export {}` as well as `export type {}`. - So it is not so useful to report `export {}`. - - Contributed by @Conaclos - -#### Bug fixes - -- [noControlCharactersInRegex](https://www.biomejs.dev/linter/rules/no-control-characters-in-regex) now corretcly handle `\u` escapes in unicode-aware regexes. - - Previously, the rule didn't consider regex with the `v` flags as unicode-aware regexes. - Moreover, `\uhhhh` was not handled in unicode-aware regexes. - - Contributed by @Conaclos - -- [noControlCharactersInRegex](https://www.biomejs.dev/linter/rules/no-control-characters-in-regex) now reports control characters and escape sequence of control characters in string regexes. Contributed by @Conaclos - -- `noExcessiveNestedTestSuites`: fix an edge case where the rule would alert on heavily nested zod schemas. Contributed by @dyc3 - -- `noExtraNonNullAssertion` no longer reports a single non-null assertion enclosed in parentheses ([#3352](https://github.com/biomejs/biome/issues/3352)). Contributed by @Conaclos - -- [noMultipleSpacesInRegularExpressionLiterals](https://biomejs.dev/linter/rules/no-multiple-spaces-in-regular-expression-literals/) now correctly provides a code fix when Unicode characters are used. Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) no longer report redeclartions for lexically scoped function declarations [#3664](https://github.com/biomejs/biome/issues/3664). - - In JavaScript strict mode, function declarations are lexically scoped: - they cannot be accessed outside the block where they are declared. - - In non-strict mode, function declarations are hoisted to the top of the enclosing function or global scope. - - Previously Biome always hoisted function declarations. - It now takes into account whether the code is in strict or non strict mode. - - Contributed by @Conaclos - -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) now ignores self package imports. - - Given teh following `package.json`: - - ```json - { - "name": "my-package", - "main": "index.js" - } - ``` - - The following import is no longer reported by the rule: - - ```js - import * as mod from "my-package"; - ``` - - Contributed by @Conaclos - -- Fix [[#3149](https://github.com/biomejs/biome/issues/3149)] crashes that occurred when applying the `noUselessFragments` unsafe fixes in certain scenarios. Contributed by @unvalley - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) no longer reports a variable named as the function expression where it is declared. Contributed by @Conaclos - -- `useAdjacentOverloadSignatures` no longer reports a `#private` class member and a public class member that share the same name ([#3309](https://github.com/biomejs/biome/issues/3309)). - - The following code is no longer reported: - - ```js - class C { - #f() {} - g() {} - f() {} - } - ``` - - Contributed by @Conaclos - -- [useAltText](https://www.biomejs.dev/linter/rules/use-alt-text) n olonger requests alt text for elements hidden from assistive technologies ([#3316](https://github.com/biomejs/biome/issues/3316)). Contributed by @robintown - -- [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now accepts applying custom convention on abstract classes. Contributed by @Conaclos - -- [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) no longer suggests an empty fix when a name doesn't match strict Pascal case ([#3561](https://github.com/biomejs/biome/issues/3561)). - - Previously the following code led `useNamingConvention` to suggest an empty fix. - The rule no longer provides a fix for this case. - - ```ts - type AAb = any - ``` - - Contributed by @Conaclos - -- [useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) no longer provides fixes for global TypeScript declaration files. - - Global TypeScript declaration files have no epxorts and no imports. - All the declared types are available in all files of the project. - Thus, it is not safe to propose renaming only in the declaration file. - - Contributed by @Conaclos - -- [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) lint error with Template literals ([#3394](https://github.com/biomejs/biome/issues/3394)). Contributed by @hangaoke1 - -- [useValidAriaValues](https://biomejs.dev/linter/rules/use-valid-aria-values/) now correctly check property types ([3748](https://github.com/biomejs/biome/issues/3748)). - - Properties that expect a string now accept arbitrary text. - An identifiers can now be made up of any characters except ASCII whitespace. - An identifier list can now be separated by any ASCII whitespace. - - Contributed by @Conaclos - -### Parser - -#### Enhancements - -- The JSON parser now allows comments in `turbo.json` and `jest.config.json`. Contributed by @Netail and @Conaclos - -- The JSON parser now allows comments in files with the `.json` extension under the `.vscode` and `.zed` directories. - - Biome recognizes are well known JSON files that allows comments and/or trailing commas. - Previously, Biome did not recognize JSON files under the `.vscode` and the `.zed` directories as JSON files that allow comments. - You had to configure Biome to recognize them: - - ```json - { - "overrides": [ - { - "include": ["**/.vscode/*.json", "**/.zed/*.json"], - "json": { "parser": { "allowComments": true } } - } - ] - } - ``` - - This override is no longer needed! - Note that JSON files under the `.vscode` and the `.zed` directories don't accept trailing commas. - - Contributed by @Conaclos - -#### Bug fixes - -- The CSS parser now accepts emoji in identifiers ([3627](https://github.com/biomejs/biome/issues/3627)). - - The following code is now correctly parsed: - - ```css - p { - --🥔-color: red; - color: var(--🥔-color); - } - ``` - - Contributed by @Conaclos - -- Fix [#3287](https://github.com/biomejs/biome/issues/3287) nested selectors with pseudo-classes. Contributed by @denbezrukov - -- Fix [#3349](https://github.com/biomejs/biome/issues/3349) allow CSS multiple ampersand support. Contributed by @denbezrukov - - ```css - .class { - && { - color: red; - } - } - ``` - -- Fix [#3410](https://github.com/biomejs/biome/issues/3410) by correctly parsing break statements containing keywords. - ```js - out: while (true) { - break out; - } - ``` - Contributed by @ah-yu - -- Fix [#3464](https://github.com/biomejs/biome/issues/3464) by enabling JSX in `.vue` files that use the `lang='jsx'` or `lang='tsx'` attribute. Contributed by @ematipico - - -## v1.8.3 (2024-06-27) - -### CLI - -#### Bug fixes - -- Fix [#3104](https://github.com/biomejs/biome/issues/3104) by suppressing node warnings when using `biome migrate`. Contributed by @SuperchupuDev - -- Force colors to be off when using the GitHub reporter to properly create annotations in GitHub actions ([#3148](https://github.com/biomejs/biome/issues/3148)). Contributed by @Sec-ant - -### Parser - -#### Bug fixes - -- Implement [CSS unicode range](https://github.com/biomejs/biome/pull/3251). Contributed by @denbezrukov - -### Formatter - -#### Bug fixes - -- Fix [#3184](https://github.com/biomejs/biome/issues/3184) CSS formatter converts custom identifiers to lowercase. Contributed by @denbezrukov -- Fix [#3256](https://github.com/biomejs/biome/issues/3256) constant crashes when editing css files #3256. Contributed by @denbezrukov - -### Linter - -#### New features - -- Add `nursery/useDeprecatedReason` rule. Contributed by @vohoanglong0107. -- Add [nursery/noExportedImports](https://biomejs.dev/linter/rules/no-exported-imports/). Contributed by @Conaclos - -#### Enhancements - -- Implement [suggestedExtensions option](https://github.com/biomejs/biome/pull/3274) for `useImportExtensions` rule. Contributed by @drdaemos - -#### Bug fixes - -- `useConsistentArrayType` and `useShorthandArrayType` now ignore `Array` in the `extends` and `implements` clauses. Fix [#3247](https://github.com/biomejs/biome/issues/3247). Contributed by @Conaclos -- Fixes [#3066](https://github.com/biomejs/biome/issues/3066) by taking into account the dependencies declared in the `package.json`. Contributed by @ematipico -- The code action of the `useArrowFunction` rule now preserves a trailing comma when there is only a single type parameter in the arrow function and JSX is enabled. Fixes [#3292](https://github.com/biomejs/biome/issues/3292). Contributed by @Sec-ant - -#### Enhancements -- Enhance tailwind sorting lint rule [#1274](https://github.com/biomejs/biome/issues/1274) with variant support. - - Every preconfigured variant is assigned a `weight` that concurs on establishing the output sorting order. - Since nesting variants on the same utility class is possible, the resulting `weight` is the Bitwise XOR of all the variants weight for that class. - Dynamic variants (e.g. `has-[.custom-class]`, `group-[:checked]`) are also supported and they take the `weight` of their base variant name the custom value attached (e.g. `has-[.custom-class]` takes `has` weight). - Arbitrary variants (e.g. `[&nth-child(2)]`) don't have a weight assigned and they are placed after every known variant. - Classes with the same amount of arbitrary variants follow lexicographical order. The class that has the highest number of nested arbitrary variants is placed last. - Screen variants (e.g. `sm:`, `max-md:`, `min-lg:`) are not supported yet. - - Contributed by @lutaok - -## v1.8.2 (2024-06-20) - -### CLI - -#### Bug fixes - -- Fix [#3201](https://github.com/biomejs/biome/issues/3201) by correctly injecting the source code of the file when printing the diagnostics. Contributed by @ematipico -- Fix [#3179](https://github.com/biomejs/biome/issues/3179) where comma separators are not correctly removed after running `biome migrate` and thus choke the parser. Contributed by @Sec-ant -- Fix [#3232](https://github.com/biomejs/biome/issues/3232) by correctly using the colors set by the user. Contributed by @ematipico - -#### Enhancement - -- Reword the reporter message `No fixes needed` to `No fixes applied`. - - The former message is misleading when there're still errors or warnings in the files that should be taken care of manually. For example: - - ```block - Checked 2 files in
:
foo
}; -}); - -// invalid -[].map((item) => { - return <>{item.condition ?
:
foo
}; -}); -``` -- `noExcessiveNestedTestSuites` no longer erroneously alerts on `describe` calls that are not invoking the global `describe` function. [#2599](https://github.com/biomejs/biome/issues/2599) Contributed by @dyc3 -```js -// now valid -z.object({}) - .describe('') - .describe('') - .describe('') - .describe('') - .describe('') - .describe(''); -``` -- `noEmptyBlockStatements` no longer reports empty constructors using typescript parameter properties. [#3005](https://github.com/biomejs/biome/issues/3005) Contributed by @dyc3 -- `noEmptyBlockStatements` no longer reports empty private or protected constructors. Contributed by @dyc3 - -- [noExportsInTest](https://biomejs.dev/linter/rules/no-exports-in-test/) rule no longer treats files with in-source testing as test files https://github.com/biomejs/biome/issues/2859. Contributed by @ah-yu -- [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) now keeps leading and trailing spaces when applying the code action inside template literals: - - ``` - i Unsafe fix: Sort the classes. - - 1 1 │ <> - 2 │ - → - 2 │ + → - 3 3 │
- 4 4 │ - ``` -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) is correctly triggered when running `biome ci`. Contributed by @ematipico -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer panics when a certain combination of characters is typed. Contributed by @ematipico - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) no logger alerts on `arguments` object in a function scope. Contributed by @ah-yu -### Parser - -#### Enhancements - -- `lang="tsx"` is now supported in Vue Single File Components. [#2765](https://github.com/biomejs/biome/issues/2765) Contributed by @dyc3 - -#### Bug fixes - -- The `const` modifier for type parameters is now accepted for TypeScript `new` signatures ([#2825](https://github.com/biomejs/biome/issues/2825)). - - The following code is now correctly parsed: - - ```ts - interface I { - new(x: T): T - } - ``` - - Contributed by @Conaclos - -- Some invalid TypeScript syntax caused the Biome parser to crash. - - The following invalid syntax no longer causes the Biome parser to crash: - - ```ts - declare using x: null; - declare qwait using x: null; - ``` - - Contributed by @Conaclos - -## 1.7.3 (2024-05-06) - -### CLI - -#### Bug fixes - -- The [stdin-file-path](https://biomejs.dev/guides/integrate-in-editor/#use-stdin) option now works correctly for Astro/Svelte/Vue files ([#2686](https://github.com/biomejs/biome/pull/2686)) - - Fix [#2225](https://github.com/biomejs/biome/issues/2225) where lint output become empty for Vue files. - - Contributed by @tasshi-me - -- `biome migrate eslint` now correctly resolve `@scope/eslint-config` ([#2705](https://github.com/biomejs/biome/issues/2705)). Contributed by @Conaclos - -### Linter - -#### New features - -- Add [nursery/noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/). -- Add [nursery/useExplicitLengthCheck](https://biomejs.dev/linter/rules/use-explicit-length-check/). Contributed by @minht11 - -- `useExhaustiveDependencies` now recognizes (some) dependencies that change on - every render ([#2374](https://github.com/biomejs/biome/issues/2374)). - Contributed by @arendjr - -#### Bug fixes - -- [noBlankTarget](https://biomejs.dev/linter/rules/no-blank-target/) no longer hangs when applying a code fix ([#2675](https://github.com/biomejs/biome/issues/2675)). - - Previously, the following code made Biome hangs when applying a code fix. - - ```jsx - - ``` - - Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) no longer panics on conditional type ([#2659](https://github.com/biomejs/biome/issues/2659)). - - This is a regression introduced by [#2394](https://github.com/biomejs/biome/issues/2394). - This regression makes `noRedeclare` panics on every conditional types with `infer` bindings. - - Contributed by @Conaclos - -- [noUnusedLabels](https://biomejs.dev/linter/rules/no-unused-labels/) and [noConfusingLabels](https://biomejs.dev/linter/rules/no-confusing-labels/) now ignore svelte reactive statements ([#2571](https://github.com/biomejs/biome/issues/2571)). - - The rules now ignore reactive Svelte blocks in Svelte components. - - ```svelte - - ``` - - Contributed by @Conaclos - -- [useExportType](https://biomejs.dev/linter/rules/use-export-type/) no longer removes leading comments ([#2685](https://github.com/biomejs/biome/issues/2685)). - - Previously, `useExportType` removed leading comments when it factorized the `type` qualifier. - It now provides a code fix that preserves the leading comments: - - ```diff - - export { - + export type { - /**leading comment*/ - - type T - + T - } - ``` - - Contributed by @Conaclos - -- [useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable/) no longer reports false positive when iterating on non-jsx items ([#2590](https://github.com/biomejs/biome/issues/2590)). - - The following snipet of code no longer triggers the rule: - - ```jsx - <>{data.reduce((total, next) => total + next, 0)} - ``` - - Contributed by @dyc3 - -- Fix typo by renaming `useConsistentBuiltinInstatiation` to `useConsistentBuiltinInstantiation` - Contributed by @minht11 -- Fix the rule `useSingleCaseStatement` including `break` statements when counting the number of statements in a `switch` statement (#2696) - - -## 1.7.2 (2024-04-30) - -### Analyzer - -#### Bug fixes - -- Import sorting now ignores side effect imports ([#817](https://github.com/biomejs/biome/issues/817)). - - A side effect import consists now in its own group. - This ensures that side effect imports are not reordered. - - Here is an example of how imports are now sorted: - - ```diff - import "z" - - import { D } from "d"; - import { C } from "c"; - + import { D } from "d"; - import "y" - import "x" - - import { B } from "b"; - import { A } from "a"; - + import { B } from "b"; - import "w" - ``` - - Contributed by @Conaclos - -- Import sorting now adds spaces where needed ([#1665](https://github.com/biomejs/biome/issues/1665)) - Contributed by @Conaclos - -### CLI - -#### Bug fixes - -- `biome migrate eslint` now handles cyclic references. - - Some plugins and configurations export objects with cyclic references. - This causes `biome migrate eslint` to fail or ignore them. - These edge cases are now handled correctly. - - Contributed by @Conaclos - -### Formatter - -#### Bug fixes - -- Correctly handle placement of comments inside named import clauses. [#2566](https://github.com/biomejs/biome/pull/2566). Contributed by @ah-yu - -### Linter - -#### New features - -- Add [nursery/noReactSpecificProps](https://biomejs.dev/linter/rules/no-react-specific-props/). - Contributed by @marvin-j97 - -- Add [noUselessUndefinedInitialization](https://biomejs.dev/linter/rules/no-useless-undefined-initialization/). - Contributed by @lutaok - -- Add [nursery/useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/). - Contributed by @Kazuhiro-Mimaki - -- Add [nursery/useConsistentBuiltinInstatiation](https://biomejs.dev/linter/rules/use-consistent-builtin-instantiation/). - Contributed by @minht11 - -- Add [nursery/useDefaultSwitchClause](https://biomejs.dev/linter/rules/use-default-switch-clause/). - Contributed by @michellocana - -#### Bug fixes - -- [noDuplicateJsonKeys](https://biomejs.dev/linter/rules/no-duplicate-json-keys/) no longer crashes when a JSON file contains an unterminated string ([#2357](https://github.com/biomejs/biome/issues/2357)). - Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) now reports redeclarations of parameters in a functions body ([#2394](https://github.com/biomejs/biome/issues/2394)). - - The rule was unable to detect redeclarations of a parameter or a type parameter in the function body. - The following two redeclarations are now reported: - - ```ts - function f(a) { - type T = number; // redeclaration - const a = 0; // redeclaration - } - ``` - - Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) no longer reports overloads in object types ([#2608](https://github.com/biomejs/biome/issues/2608)). - - The rule no longer report redeclarations in the following code: - - ```ts - type Overloads = { - ({ a }: { a: number }): number, - ({ a }: { a: string }): string, - }; - ``` - - Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) now merge default function export declarations and types ([#2372](https://github.com/biomejs/biome/issues/2372)). - - The following code is no longer reported as a redeclaration: - - ```ts - interface Foo {} - export default function Foo() {} - ``` - - Contributed by @Conaclos - -- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) no longer reports variable-only and type-only exports ([#2637](https://github.com/biomejs/biome/issues/2637)). - Contributed by @Conaclos - -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer crash Biome when encountering a malformed conditional type ([#1695](https://github.com/biomejs/biome/issues/1695)). - Contributed by @Conaclos - -- [useConst](https://biomejs.dev/linter/rules/use-const/) now ignores a variable that is read before its assignment. - - Previously, the rule reported the following example: - - ```js - let x; - x; // read - x = 0; // write - ``` - - It is now correctly ignored. - - Contributed by @Conaclos - -- [useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type/) now suggests correct code fixes when parentheses are required ([#2595](https://github.com/biomejs/biome/issues/2595)). - - Previously, the rule didn't add parentheses when they were needed. - It now adds parentheses when the function signature is inside an array, a union, or an intersection. - - ```diff - - type Union = { (): number } | string; - + type Union = (() => number) | string; - ``` - - Contributed by @Conaclos - -- [useTemplate](https://biomejs.dev/linter/rules/use-template/) now correctly escapes strings ([#2580](https://github.com/biomejs/biome/issues/2580)). - - Previously, the rule didn't correctly escape characters preceded by an escaped character. - - Contributed by @Conaclos - -- [noMisplacedAssertion](https://biomejs.dev/linter/rules/no-misplaced-assertion/) now allow these matchers - - - `expect.any()` - - `expect.anything()` - - `expect.closeTo` - - `expect.arrayContaining` - - `expect.objectContaining` - - `expect.stringContaining` - - `expect.stringMatching` - - `expect.extend` - - `expect.addEqualityTesters` - - `expect.addSnapshotSerializer` - - Contributed by @fujiyamaorange - -### Parser - -#### Bug fixes - -- The language parsers no longer panic on unterminated strings followed by a newline and a space ([#2606](https://github.com/biomejs/biome/issues/2606), [#2410](https://github.com/biomejs/biome/issues/2410)). - - The following example is now parsed without making Biome panics: - - ``` - " - " - ``` - - Contributed by @Conaclos - - -## 1.7.1 (2024-04-22) - -### Editors - -#### Bug fixes - -- Fix [#2403](https://github.com/biomejs/biome/issues/2403) by printing the errors in the client console. Contributed by @ematipico - -### Formatter - -#### Bug fixes - -- Add parentheses for the return expression that has leading multiline comments. [#2504](https://github.com/biomejs/biome/pull/2504). Contributed by @ah-yu - -- Correctly format dangling comments of continue statements. [#2555](https://github.com/biomejs/biome/pull/2555). Contributed by @ah-yu - -- Prevent comments from being eaten by the formatter [#2578](https://github.com/biomejs/biome/pull/2578). Now the comments won't be eaten for the following code: - ```js - console.log((a,b/* comment */)); - ``` - Contributed by @ah-yu - -- Correctly format nested union type to avoid reformatting issue. [#2628](https://github.com/biomejs/biome/pull/2628). Contributed by @ah-yu - -### Linter - -#### Bug fixes - -- Fix case where `jsxRuntime` wasn't being respected by `useImportType` rule ([#2473](https://github.com/biomejs/biome/issues/2473)).Contributed by @arendjr -- Fix [#2460](https://github.com/biomejs/biome/issues/2460), where the rule `noUselessFragments` was crashing the linter in some cases. Now cases like these are correctly handled: - ```jsx - callFunction(<>{bar}) - ``` - Contributed by @ematipico -- Fix [#2366](https://github.com/biomejs/biome/issues/2366), where `noDuplicateJsonKeys` incorrectly computed the kes to highlight. Contributed by @ematipico -#### Enhancements - -- The rule `noMisplacedAssertions` now considers valid calling `expect` inside `waitFor`: - ```js - import { waitFor } from '@testing-library/react'; - - await waitFor(() => { - expect(111).toBe(222); - }); - ``` - Contributed by @ematipico - - -## 1.7.0 (2024-04-15) - -### Analyzer - -#### Bug fixes - -- Now Biome can detect the script language in Svelte and Vue script blocks more reliably ([#2245](https://github.com/biomejs/biome/issues/2245)). Contributed by @Sec-ant - -- `useExhaustiveDependencies` no longer reports recursive calls as missing - dependencies ([#2361](https://github.com/biomejs/biome/issues/2361)). - Contributed by @arendjr - -- `useExhaustiveDependencies` correctly reports missing dependencies declared - using function declarations ([#2362](https://github.com/biomejs/biome/issues/2362)). - Contributed by @arendjr - -- Biome now can handle `.svelte` and `.vue` files with `CRLF` as the end-of-line sequence. Contributed by @Sec-ant - -- `noMisplacedAssertion` no longer reports method calls by `describe`, `test`, `it` objects (e.g. `test.each([])()`) ([#2443](https://github.com/biomejs/biome/issues/2443)). Contributed by @unvalley. - -- Biome now can handle `.vue` files with [generic components](https://vuejs.org/api/sfc-script-setup#generics) ([#2456](https://github.com/biomejs/biome/issues/2456)). - ```vue - - ``` - Contributed by @Sec-ant - -#### Enhancements - -- Complete the well-known file lists for JSON-like files. Trailing commas are allowed in `.jsonc` files by default. Some well-known files like `tsconfig.json` and `.babelrc` don't use the `.jsonc` extension but still allow comments and trailing commas. While others, such as `.eslintrc.json`, only allow comments. Biome is able to identify these files and adjusts the `json.parser.allowTrailingCommas` option accordingly to ensure they are correctly parsed. Contributed by @Sec-ant - -- Fix dedent logic inconsistent with prettier where the indent-style is space and the indent-width is not 2. Contributed by @mdm317 - -### CLI - -#### New features - -- Add a command to migrate from ESLint - - `biome migrate eslint` allows you to migrate an ESLint configuration to Biome. - The command supports [legacy ESLint configurations](https://eslint.org/docs/latest/use/configure/configuration-files) and [new flat ESLint configurations](https://eslint.org/docs/latest/use/configure/configuration-files-new). - Legacy ESLint configurations using the YAML format are not supported. - - When loading a legacy ESLint configuration, Biome resolves the `extends` field. - It resolves both shared configurations and plugin presets! - To do this, it invokes _Node.js_. - - Biome relies on the metadata of its rules to determine the [equivalent rule of an ESLint rule](https://biomejs.dev/linter/rules-sources/). - A Biome rule is either inspired or roughly identical to an ESLint rules. - By default, inspired and nursery rules are excluded from the migration. - You can use the CLI flags `--include-inspired` and `--include-nursery` to migrate them as well. - - Note that this is a best-effort approach. - You are not guaranteed to get the same behavior as ESLint. - - Given the following ESLint configuration: - - ```json - { - "ignore_patterns": ["**/*.test.js"], - "globals": { "var2": "readonly" }, - "rules": { - "eqeqeq": "error" - }, - "overrides": [{ - "files": ["lib/*.js"], - "rules": { - "default-param-last": "off" - } - }] - } - ``` - - `biome migrate eslint --write` changes the Biome configuration as follows: - - ```json - { - "linter": { - "rules": { - "recommended": false, - "suspicious": { - "noDoubleEquals": "error" - } - } - }, - "javascript": { "globals": ["var2"] }, - "overrides": [{ - "include": ["lib/*.js"], - "linter": { - "rules": { - "style": { - "useDefaultParameterLast": "off" - } - } - } - }] - } - ``` - - Also, if the working directory contains `.eslintignore`, then Biome migrates the glob patterns. - Nested `.eslintignore` in subdirectories and negated glob patterns are not supported. - - If you find any issue, please don't hesitate to report them. - - Contributed by @Conaclos - -- Added two new options to customise the emitted output of the CLI: `--reporter=json` and `--reporter=json-pretty`. With `--reporter=json`, the diagnostics and the - summary will be printed in the **terminal** in JSON format. With `--reporter=json-pretty`, you can print the same information, but formatted using the same options of your configuration. - - NOTE: the shape of the JSON is considered experimental, and the shape of the JSON might change in the future. - -
- Example of output when running `biome format` command - ```json - { - "summary": { - "changed": 0, - "unchanged": 1, - "errors": 1, - "warnings": 0, - "skipped": 0, - "suggestedFixesSkipped": 0, - "diagnosticsNotPrinted": 0 - }, - "diagnostics": [ - { - "category": "format", - "severity": "error", - "description": "Formatter would have printed the following content:", - "message": [ - { - "elements": [], - "content": "Formatter would have printed the following content:" - } - ], - "advices": { - "advices": [ - { - "diff": { - "dictionary": " statement();\n", - "ops": [ - { "diffOp": { "delete": { "range": [0, 2] } } }, - { "diffOp": { "equal": { "range": [2, 12] } } }, - { "diffOp": { "delete": { "range": [0, 2] } } }, - { "diffOp": { "equal": { "range": [12, 13] } } }, - { "diffOp": { "delete": { "range": [0, 2] } } }, - { "diffOp": { "insert": { "range": [13, 15] } } } - ] - } - } - ] - }, - "verboseAdvices": { "advices": [] }, - "location": { - "path": { "file": "format.js" }, - "span": null, - "sourceCode": null - }, - "tags": [], - "source": null - } - ], - "command": "format" - } - ``` -
- -- Added new `--staged` flag to the `check`, `format` and `lint` subcommands. - - This new option allows users to apply the command _only_ to the files that are staged (the - ones that will be committed), which can be very useful to simplify writing git hook scripts - such as `pre-commit`. Contributed by @castarco - -#### Enhancements - -- Improve support of `.prettierignore` when migrating from Prettier - - Now, Biome translates most of the glob patterns in `.prettierignore` to the equivalent Biome ignore pattern. - Only negated glob patterns are not supported. - - Contributed by @Conaclos - -- Support JavaScript configuration files when migrating from Prettier - - `biome migrate prettier` is now able to migrate Prettier configuration files - ending with `js`, `mjs`, or `cjs` extensions. - To do this, Biome invokes Node.js. - - Also, embedded Prettier configurations in `package.json` are now supported. - - Contributed by @Conaclos - -- Support `overrides` field in Prettier configuration files when migrating from Prettier. - Contributed by @Conaclos - -- Support passing a file path to the `--config-path` flag or the `BIOME_CONFIG_PATH` environment variable. - - Now you can pass a `.json`/`.jsonc` file path with any filename to the `--config-path` flag or the - `BIOME_CONFIG_PATH` environment variable. This will disable the configuration auto-resolution and Biome - will try to read the configuration from the said file path ([#2265](https://github.com/biomejs/biome/issues/2265)). - - ```shell - biome format --config-path=../biome.json ./src - ``` - - Contributed by @Sec-ant - -#### Bug fixes - -- Biome now tags the diagnostics emitted by `organizeImports` and `formatter` with correct severity levels, so they will be properly filtered by the flag `--diagnostic-level` ([#2288](https://github.com/biomejs/biome/issues/2288)). Contributed by @Sec-ant - -- Biome now correctly filters out files that are not present in the current directory when using the `--changed` flag [#1996](https://github.com/biomejs/biome/issues/1996). Contributed by @castarco - -- Biome now skips traversing `fifo` or `socket` files ([#2311](https://github.com/biomejs/biome/issues/2311)). Contributed by @Sec-ant - -- Biome now resolves configuration files exported from external libraries in `extends` from the working directory (CLI) or project root (LSP). This is the documented behavior and previous resolution behavior is considered as a bug ([#2231](https://github.com/biomejs/biome/issues/2231)). Contributed by @Sec-ant - -### Configuration - -#### Bug fixes - -- Now setting group level `all` to `false` can disable recommended rules from that group when top level `recommended` is `true` or unset. Contributed by @Sec-ant - -- Biome configuration files can correctly extends `.jsonc` configuration files now ([#2279](https://github.com/biomejs/biome/issues/2279)). Contributed by @Sec-ant - -- Fixed the JSON schema for React hooks configuration ([#2396](https://github.com/biomejs/biome/issues/2396)). Contributed by @arendjr - -#### Enhancements - -- Biome now displays the location of a parsing error for its configuration file ([#1627](https://github.com/biomejs/biome/issues/1627)). - - Previously, when Biome encountered a parsing error in its configuration file, - it didn't indicate the location of the error. - It now displays the name of the configuration file and the range where the error occurred. - - Contributed by @Conaclos - -- `options` is no longer required for rules without any options ([#2313](https://github.com/biomejs/biome/issues/2313)). - - Previously, the JSON schema required to set `options` to `null` when an object is used to set the diagnostic level of a rule without any option. - However, if `options` is set to `null`, Biome emits an error. - - The schema is now fixed and it no longer requires specifying `options`. - This makes the following configuration valid: - - ```json - { - "linter": { - "rules": { - "style": { - "noDefaultExport": { - "level": "off" - } - } - } - } - } - ``` - - Contributed by @Conaclos - -### Editors - -#### Bug fixes - -- Biome extension is now able to parse the JSX syntax in files that associated with the `javascript` [language identifier](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem). This is an ad hoc fix, because [in the React world, `.js` files are allowed to include JSX syntax](https://github.com/facebook/create-react-app/issues/87#issuecomment-234627904), and these files are often associated with the `javascript` language identifier in most of the editors. Plus, [some editor extensions](https://github.com/michaelgmcd/vscode-language-babel/blob/8b3a472748ad07c99dc022b66795c9eb46be4ccb/package.json#L63-L80) will also associate `.jsx` files with the `javascript` language identifier. Relative links: [discussion](https://github.com/biomejs/biome/discussions/838#discussioncomment-9047539), [#2085](https://github.com/biomejs/biome/issues/2085). Contributed by @Sec-ant - -### Formatter - -#### Bug fixes - -- Fix [#2291](https://github.com/biomejs/biome/issues/2291) by correctly handle comment placement for JSX spread attributes and JSX spread children. Contributed by @ah-yu - -### JavaScript APIs - -### Linter - -#### Promoted rules - -New rules are incubated in the nursery group. -Once stable, we promote them to a stable group. -The following rules are promoted: - -- [complecity/noExcessiveNestedTestSuites](https://biomejs.dev/linter/rules/no-excessive-nested-test-suites) -- [complexity/noUselessTernary](https://biomejs.dev/linter/rules/no-useless-ternary) -- [correctness/useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable) -- [performance/noBarrelFile](https://biomejs.dev/linter/rules/no-barrel-file/) -- [performance/noReExportAll](https://biomejs.dev/linter/rules/no-re-export-all/) -- [style/noNamespaceImport](https://biomejs.dev/linter/rules/no-namespace-import/) -- [style/useNodeAssertStrict](https://biomejs.dev/linter/rules/use-node-assert-strict/) -- [suspicious/noDuplicateTestHooks](https://biomejs.dev/linter/rules/no-duplicate-test-hooks/) -- [suspicious/noExportsInTest](https://biomejs.dev/linter/rules/no-exports-in-test/) -- [suspicious/noFocusedTests](https://biomejs.dev/linter/rules/no-focused-tests/) -- [suspicious/noSkippedTests](https://biomejs.dev/linter/rules/no-skipped-tests/) -- [suspicious/noSuspiciousSemicolonInJsx](https://biomejs.dev/linter/rules/no-suspicious-semicolon-in-jsx) - -#### New features - -- Add a new option `jsxRuntime` to the `javascript` configuration. When set to `reactClassic`, the [noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports) and [useImportType](https://biomejs.dev/linter/rules/use-import-type) rules use this information to make exceptions for the React global that is required by the React Classic JSX transform. - - This is only necessary for React users who haven't upgraded to the [new JSX transform](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). - - Contributed by @Conaclos and @arendjr - -- Implement [#2043](https://github.com/biomejs/biome/issues/2043): The React rule [`useExhaustiveDependencies`](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/) is now also compatible with Preact hooks imported from `preact/hooks` or `preact/compat`. Contributed by @arendjr - -- Add rule [noFlatMapIdentity](https://biomejs.dev/linter/rules/no-flat-map-identity) to disallow unnecessary callback use on `flatMap`. Contributed by @isnakode - -- Add rule [noConstantMathMinMaxClamp](https://biomejs.dev/linter/rules/no-constant-math-min-max-clamp), which disallows using `Math.min` and `Math.max` to clamp a value where the result itself is constant. Contributed by @mgomulak - -#### Enhancements - -- [style/useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention/) now allows prefixing a filename with `+` ([#2341](https://github.com/biomejs/biome/issues/2341)). - - This is a convention used by [Sveltekit](https://kit.svelte.dev/docs/routing#page) and [Vike](https://vike.dev/route). - - Contributed by @Conaclos - -- [style/useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now accepts `PascalCase` for local and top-level variables. - - This allows supporting local variables that hold a component or a regular class. - The following code is now accepted: - - ```tsx - function loadComponent() { - const Component = getComponent(); - return ; - } - ``` - - Contributed by @Conaclos - -- [complexity/useLiteralKeys](https://biomejs.dev/linter/rules/use-literal-keys/) no longer report computed properties named `__proto__` ([#2430](https://github.com/biomejs/biome/issues/2430)). - - In JavaScript, `{["__proto__"]: null}` and `{__proto__: null}` have not the same semantic. - The first code set a regular property to `null`. - The second one set the prototype of the object to `null`. - See the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) for more details. - - The rule now ignores computed properties named `__proto__`. - - Contributed by @Conaclos - -#### Bug fixes - -- Lint rules `useNodejsImportProtocol`, `useNodeAssertStrict`, `noRestrictedImports`, `noNodejsModules` will no longer check `declare module` statements anymore. Contributed by @Sec-ant - -- [style/useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now accepts any case for variables from object destructuring ([#2332](https://github.com/biomejs/biome/issues/2332)). - - The following name is now ignored: - - ```js - const { Strange_Style } = obj; - ``` - - Previously, the rule renamed this variable. This led to a runtime error. - - Contributed by @Conaclos - -### Parser - -#### Bug fixes - -- Fixed an issue when Unicode surrogate pairs were encoded in JavaScript strings - using an escape sequence ([#2384](https://github.com/biomejs/biome/issues/2384)). - Contributed by @arendjr - - -## 1.6.4 (2024-04-03) - -### Analyzer - -#### Bug fixes - -- An operator with no spaces around in a binary expression no longer breaks the js analyzer ([#2243](https://github.com/biomejs/biome/issues/2243)). Contributed by @Sec-ant - -### CLI - -#### Bug fixes - -- Fix the printed error count ([#2048](https://github.com/biomejs/biome/issues/2048)). Contributed by @Sec-ant - -### Configuration - -#### Bug fixes - -- Correctly calculate enabled rules in lint rule groups. Now a specific rule belonging to a group can be enabled even if its group-level preset option `recommended` or `all` is `false` ([#2191](https://github.com/biomejs/biome/issues/2191)). Contributed by @Sec-ant - -### Editors - -#### Bug fixes - -- Fix the unexpected code deletion and repetition when `quickfix.biome` is enabled and some `import`-related rules are applied ([#2222](https://github.com/biomejs/biome/issues/2222), [#688](https://github.com/biomejs/biome/issues/688), [#1015](https://github.com/biomejs/biome/issues/1015)). Contributed by @Sec-ant - -### Linter - -#### New features - -- Add [nursery/noMisplacedAssertion](https://biomejs.dev/linter/rules/no-misplaced-assertion/). COntributed by @ematipico - -#### Bug fixes - -- Fix [#2211](https://github.com/biomejs/biome/issues/2211). noChildrenProp should work fine when children pass as a prop in a new line. Contributed by @fireairforce - -- Fix [#2248](https://github.com/biomejs/biome/issues/2248). `lint/a11y/useButtonType` should not trigger when button element with spread attribute. Contributed by @fireairforce - -- Fix [#2216](https://github.com/biomejs/biome/issues/2216). `lint/style/useNamingConvention` should not ignore JSX Component name binding. Contributed by @fireairforce - -#### Enhancements - -- Add support for object property members in the rule `useSortedClasses`. Contributed by @ematipico - -### Parser - -- The parser doesn't throw any error when the frontmatter of `.astro` files contains an illegal return: - - ```astro - --- - const condition = true; - if (condition) { - return "Something"; - } - --- -
- ``` - Contributed by @ematipico - -## 1.6.3 (2024-03-25) - -### CLI - -#### Bug fixes - -- Fix configuration resolution. Biome is now able to correctly find the `biome.jsonc` configuration file when `--config-path` is explicitly set ([#2164](https://github.com/biomejs/biome/issues/2164)). Contributed by @Sec-ant - -- JavaScript/TypeScript files of different variants (`.ts`, `.js`, `.tsx`, `.jsx`) in a single workspace now have stable formatting behaviors when running the CLI command in paths of different nested levels or in different operating systems ([#2080](https://github.com/biomejs/biome/issues/2080), [#2109](https://github.com/biomejs/biome/issues/2109)). Contributed by @Sec-ant - -### Configuration - -#### Bug fixes - -- Complete the documentation and overrides support for options `formatter.lineEnding`, `[language].formatter.lineEnding`, `formatter.attributePosition` and `javascript.formatter.attributePosition`. Contributed by @Sec-ant - -### Formatter - -#### Bug fixes - -- Fix [#2172](https://github.com/biomejs/biome/issues/2172) by breaking long object destructuring patterns. Contributed by @ah-yu - -### Linter - -#### New features - -- Add rule [noEvolvingTypes](https://biomejs.dev/linter/rules/no-evolving-any) to disallow variables from evolving into `any` type through reassignments. Contributed by @fujiyamaorange - -#### Enhancements - -- Rename `noSemicolonInJsx` to `noSuspiciousSemicolonInJsx`. Contributed by @fujiyamaorange - -### LSP - -#### Bug fixes - -- Quickfix action no longer autofixes lint rule errors on save when `linter` is disabled ([#2161](https://github.com/biomejs/biome/issues/2161)). Contributed by @Sec-ant -- Range formatting for Astro/Svelte/Vue doesn't place code out of place, especially when formatting on paste is enabled. Contributed by @ematipico - -## 1.6.2 (2024-03-22) - -### Analyzer - -#### Bug fixes - -- The `noSuperWithoutExtends` rule now allows for calling `super()` in derived class constructors of class expressions ([#2108](https://github.com/biomejs/biome/issues/2108)). Contributed by @Sec-ant - -- Fix discrepancies on file source detection. Allow module syntax in `.cts` files ([#2114](https://github.com/biomejs/biome/issues/2114)). Contributed by @Sec-ant - -### CLI - -#### Bug fixes - -- Fixes [#2131](https://github.com/biomejs/biome/issues/2131), where folders were incorrectly ignored when running the command `check`. Now folders are correctly ignored based on their command. Contributed by @ematipico - -- Smoother handling of `"endOfLine": "auto"` in prettier migration: falling back to `"lf"` ([#2145](https://github.com/biomejs/biome/pull/2145)). Contributed by @eMerzh - -### Configuration - -#### Bug fixes - -- Fix enabled rules calculation. The precendence of individual rules, `all` and `recommend` presets in top-level and group-level configs is now correctly respected. More details can be seen in ([#2072](https://github.com/biomejs/biome/pull/2072)) ([#2028](https://github.com/biomejs/biome/issues/2028)). Contributed by @Sec-ant - -### Formatter - -#### Bug fixes - -- Fix [#1661](https://github.com/biomejs/biome/issues/1661). Now nested conditionals are aligned with Prettier's logic, and won't contain mixed spaces and tabs. Contributed by @ematipico - -### JavaScript APIs - -#### Enhancements - -- Support applying lint fixes when calling the `lintContent` method of the `Biome` class ([#1956](https://github.com/biomejs/biome/pull/1956)). Contributed by @mnahkies - -### Linter - -#### New features - -- Add [nursery/noDuplicateElseIf](https://biomejs.dev/linter/rules/no-duplicate-else-if/). COntributed by @mdm317 - -#### Bug fixes - -- Rule `noUndeclaredDependencies` now also validates `peerDependencies` and `optionalDependencies` ([#2122](https://github.com/biomejs/biome/issues/2122)). Contributed by @Sec-ant - -- Rule `noUndeclaredDependencies` won't check `declare module` statements anymore ([#2123](https://github.com/biomejs/biome/issues/2123)). Contributed by @Sec-ant - -- Fix [#1925](https://github.com/biomejs/biome/issues/1925). The fix for `useOptionalChain` would sometimes suggest an incorrect fix that discarded optional chaining operators on the left-hand side of logical expressions. These are now preserved. Contributed by @arendjr - -- Rule `noUndeclaredVariables` now also checks for worker globals ([#2121](https://github.com/biomejs/biome/issues/2121)). Contributed by @Sec-ant - -### LSP - -#### Bug fixes - -- Correctly parse `.jsonc` files. Contributed by @Sec-ant - -- Correctly resolve external `extends` configs. Contributed by @Sec-ant - -## 1.6.1 (2024-03-12) - -### CLI - -#### Bug fixes - -- CLI is now able to automatically search and resolve `biome.jsonc` ([#2008](https://github.com/biomejs/biome/issues/2008)). Contributed by @Sec-ant -- Fix a false positive where some files were counted as "fixed" even though they weren't modified. Contributed by @ematipico - -### Configuration - -#### Bug fixes - -- `json.formatter.trailingCommas` option now works in `overrides` ([#2009](https://github.com/biomejs/biome/issues/2009)). Contributed by @Sec-ant - -### Linter - -#### New features - -- Add rule [noDoneCallback](https://biomejs.dev/linter/rules/no-done-callback), this rule checks the function parameter of hooks & tests - for use of the done argument, suggesting you return a promise instead. Contributed by @vasucp1207 - - ```js - beforeEach(done => { - // ... - }); - ``` - -#### Bug fixes - -- [useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable) now recognizes function bodies wrapped in parentheses ([#2011](https://github.com/biomejs/biome/issues/2011)). Contributed by @Sec-ant - -- [useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type) now preserves type parameters of generic interfaces when applying fixes ([#2015](https://github.com/biomejs/biome/issues/2015)). Contributed by @Sec-ant - -- Code fixes of [useImportType](https://biomejs.dev/linter/rules/use-import-type) and [useExportType](https://biomejs.dev/linter/rules/use-export-type) now handle multiline statements ([#2041](https://github.com/biomejs/biome/issues/2041)). Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare) no longer reports type parameter and parameter with identical names ([#1992](https://github.com/biomejs/biome/issues/1992)). - - The following code is no longer reported: - - ```ts - function f(a: a) {} - ``` - - Contributed by @Conaclos - -- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare) now reports duplicate type parameters in a same declaration. - - The following type parameters are now reported as a redeclaration: - - ```ts - function f() {} - ``` - - Contributed by @Conaclos - -- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) now recognizes imports of subpath exports. - - E.g., the following import statements no longer report errors if `@mui/material` and `tailwindcss` are installed as dependencies: - - ```ts - import Button from "@mui/material/Button"; - import { fontFamily } from "tailwindcss/defaultTheme"; - ``` - - Contributed by @Sec-ant - -### Parser - -#### Bug fixes - -- JavaScript lexer is now able to lex regular expression literals with escaped non-ascii chars ([#1941](https://github.com/biomejs/biome/issues/1941)). - - Contributed by @Sec-ant - -## 1.6.0 (2024-03-08) - -### Analyzer - -#### New features - -- Add partial for `.astro` files. Biome is able to sort imports inside the frontmatter of the Astro files. Contributed - by @ematipico - - ```diff - --- - - import { getLocale } from "astro:i18n"; - - import { Code } from "astro:components"; - + import { Code } from "astro:components"; - + import { getLocale } from "astro:i18n"; - --- - -
- ``` -- Add partial for `.vue` files. Biome is able to sort imports inside the script block of Vue files. Contributed by - @nhedger - - ```diff - - - - ``` - -- Add partial for `.svelte` files. Biome is able to sort imports inside the script block of Svelte files. Contributed by - @ematipico - - ```diff - - -
- ``` - -- The analyzer now **infers** the correct quote from `javascript.formatter.quoteStyle`, if set. This means that code fixes suggested by the analyzer will use the same quote of the formatter. Contributed by @ematipico - -#### Enhancements - -- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables) ignores unused rest spread siblings. - - The following code is now valid: - - ```js - const { a, ...rest } = { a: 0, b: 1 }; - console.log(rest); - ``` - - Contributed by @ah-yu - -- Fix [#1931](https://github.com/biomejs/biome/issues/1931). Built-in React hooks such as - `useEffect()` can now be validated by the - [`useExhaustiveDependendies`](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/), even - when they're not being imported from the React library. To do so, simply configure them like - any other user-provided hooks. - - Contributed by @arendjr - -- Implemented [#1128](https://github.com/biomejs/biome/issues/1128). User-provided React hooks can - now be configured to track stable results. For example: - - ```json - "useExhaustiveDependencies": { - "level": "error", - "options": { - "hooks": [{ - "name": "useMyState", - "stableResult": [ - 1 - ] - }] - } - } - ``` - - This will allow the following to be validated: - - ```js - const [myState, setMyState] = useMyState(); - const toggleMyState = useCallback(() => { - setMyState(!myState); - }, [myState]); // Only `myState` needs to be specified here. - ``` - - Contributed by @arendjr - -#### Bug fixes - -- Fix [#1748](https://github.com/biomejs/biome/issues/1748). Now for the following case we won't provide an unsafe fix - for the `noNonNullAssertion` rule: - - ```ts - x[y.z!]; - ``` - - Contributed by @ah-yu - -- Imports that contain the protocol `:` are now sorted after the `npm:` modules, and before the `URL` modules. - Contributed by @ematipico - - ```diff - import express from "npm:express"; - - import Component from "./component.js" - - import { sortBy } from "virtual:utils"; - + import { sortBy } from "virtual:utils"; - + import Component from "./component.js" - ``` - -- Fix [#1081](https://github.com/biomejs/biome/issues/1081). The `useAwait` rule does not report `for await...of`. - Contributed by @unvalley - -- Fix [#1827](https://github.com/biomejs/biome/issues/1827) by properly analyzing nested `try-finally` statements. Contributed by @ah-yu - -- Fix [#1924](https://github.com/biomejs/biome/issues/1924) Use the correct export name to sort in the import clause. Contributed by @ah-yu -- Fix [#1805](https://github.com/biomejs/biome/issues/1805) fix formatting arrow function which has conditional expression body Contributed by @mdm317 - -- Fix [#1781](https://github.com/biomejs/biome/issues/1781) by avoiding the retrieval of the entire static member expression for the reference if the static member expression does not start with the reference. Contributed by @ah-yu - -### CLI - -#### New features - -- Add a new command `biome migrate prettier`. The command will read the file `.prettierrc`/`prettier.json` - and `.prettierignore` and map its configuration to Biome's one. - Due to the different nature of `.prettierignore` globs and Biome's globs, it's **highly** advised to make sure that - those still work under Biome. - -- Now the file name printed in the diagnostics is clickable. If you run the CLI from your editor, you can - Ctrl/ + Click on the file name, and the editor will open said file. If row and columns - are specified e.g. `file.js:32:7`, the editor will set the cursor right in that position. Contributed by @ematipico - -- Add an option `--linter` to `biome rage`. The option needs to check Biome linter configuration. Contributed by - @seitarof - -- Add an option `--formatter` to `biome rage`. The option needs to check Biome formatter configuration. Contributed by - @seitarof -- The CLI now consistently reports the number of files tha were changed, out of the total files that were analysed. Contributed by @ematipico -- The CLI now consistently shows the number of errors and warnings emitted. Contributed by @ematipico - -#### Bug fixes - -- Don't process files under an ignored directory. - - Previously, Biome processed all files in the traversed hierarchy, - even the files under an ignored directory. - Now, it completely skips the content of ignored directories. - - For now, directories cannot be ignored using `files.include` in the configuration file. - This is a known limitation that we want to address in a future release. - - For instance, if you have a project with a folder `src` and a folder `test`, - the following configuration doesn't completely ignore `test`. - - ```json - { - "files": { - "include": ["src"] - } - } - ``` - - Biome will traverse `test`, - however all files of the directory are correctly ignored. - This can result in file system errors, - if Biome encounters dangling symbolic links or files with higher permissions. - - To avoid traversing the `test` directory, - you should ignore the directory using `ignore`: - - ```json - { - "files": { - "include": ["src"], - "ignore": ["test"] - } - } - ``` - -- Fix [#1508](https://github.com/biomejs/biome/issues/1508) by excluding deleted files from being processed. Contributed - by @ematipico - -- Fix [#1173](https://github.com/biomejs/biome/issues/1173). Fix the formatting of a single instruction with commented - in a control flow body to ensure consistency. Contributed by @mdm317 - -- Fix overriding of `javascript.globals`. Contributed by @arendjr -- Fix a bug where syntax rules weren't run when pulling the diagnostics. Now Biome will emit more parsing diagnostics, - e.g. - ``` - check.js:1:17 parse/noDuplicatePrivateClassMembers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Duplicate private class member "#foo" - - > 1 │ class A { #foo; #foo } - │ ^^^^ - - ``` - Contributed by @ematipico - -- Fix [#1774](https://github.com/biomejs/biome/issues/1774) by taking into account the option `--no-errors-on-unmatched` when running the CLI using `--changed`. Contributed by @antogyn - -#### Enhancements - -- Removed a superfluous diagnostic that was printed during the linting/check phase of a file: - - ``` - test.js check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × The file contains diagnostics that needs to be addressed. - ``` - Contributed by @ematipico -- The command `format` now emits parsing diagnostics if there are any, and it will terminate with a non-zero exit code. Contributed by @ematipico - -### Configuration - -#### New features - -- Add the ability to resolve the configuration files defined inside `extends` from the `node_modules/` directory. - - If you want to resolve a configuration file that matches the specifier `@org/configs/biome`, then your `package.json` - file must look this: - - ```json - { - "name": "@org/configs", - "exports": { - "./biome": "./biome.json" - } - } - ``` - - And the `biome.json` file that "imports" said configuration, will look like this: - ```json - { - "extends": "@org/configs/biome" - } - ``` - Read the [documentation](https://biomejs.dev/guides/how-biome-works#the-extends-option) to better understand how it - works, expectations and restrictions. - -### Editors - -#### Bug fixes - -- Fix a regression where ignored files where formatted in the editor. Contributed by @ematipico -- Fix a bug where syntax rules weren't run when pulling the diagnostics. Now Biome will emit more parsing diagnostics, - e.g. - ``` - check.js:1:17 parse/noDuplicatePrivateClassMembers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Duplicate private class member "#foo" - - > 1 │ class A { #foo; #foo } - │ ^^^^ - - ``` - Contributed by @ematipico - -### Formatter - -#### New features - -- Biome now allows to format the `package.json` file. This is now the default behaviour and users can remove their - workarounds. - If you rely on other tools to format `package.json`, you'll have to ignore it via configuration. Contributed by - @pattrickrice -- New formatter option `attributePosition` that have similar behavior as - Prettier `singleAttributePerLine` [#1706](https://github.com/biomejs/biome/issues/1706). Contributed by @octoshikari -- Add partial for `.astro` files. Biome is able to format the frontmatter of the Astro files. Contributed by @ematipico - - ```diff - --- - - statement ( ); - + statement(); - --- - -
- ``` -- Add partial for `.vue` files. Biome is able to format the script block of Vue files. Contributed by @nhedger - - ```diff - - - - ``` - -- Add partial for `.svelte` files. Biome is able to format the script block of Svelte files. Contributed by @ematipico - - ```diff - - -
- ``` - -#### Enhancements - -- `composer.json`, `deno.json`, `jsconfig.json`, `package.json` and `tsconfig.json` are no longer protected files. - - This means that you can now format them. - - If you want to ignore these files, you can use the [files.ignore](https://biomejs.dev/reference/configuration/#filesignore) configuration: - - ```json - { - "files": { - "ignore": [ - "composer.json", - "jsconfig.json", - "package.json", - "tsconfig.json", - "typescript.json", - "deno.json", - "deno.jsonc" - ] - } - } - ``` - - The following files are still protected, and thus ignored: - - - `composer.lock` - - `npm-shrinkwrap.json` - - `package-lock.json` - - `yarn.lock` - - Contributed by @pattrickrice and @Conaclos - -#### Bug fixes - -- Fix [#1039](https://github.com/biomejs/biome/issues/1039). Check unicode width instead of number of bytes when - checking if regex expression is a simple argument. - - This no longer breaks. - - ```js - s(/🚀🚀/).s().s(); - ``` - - Contributed by @kalleep - -- Fix [#1218](https://github.com/biomejs/biome/issues/1218), by correctly preserving empty lines in member chains. - Contributed by @ah-yu -- Fix [#1659](https://github.com/biomejs/biome/issues/1659) and [#1662](https://github.com/biomejs/biome/issues/1662), by correctly taking into account the leading comma inside the formatter options. Contributed by @ematipico - -- Fix [#1934](https://github.com/biomejs/biome/pull/1934). Fix invalid formatting of long arrow function for AsNeeded arrow parens Contributed by @fireairforce - -### JavaScript APIs - -### Linter - -#### Promoted rules - -New rules are incubated in the nursery group. -Once stable, we promote them to a stable group. -The following rules are promoted: - -- [complexity/noEmptyTypeParameters](https://biomejs.dev/linter/rules/no-empty-type-parameters) -- [complexity/noUselessLoneBlockStatements](https://biomejs.dev/linter/rules/no-useless-lone-block-statements) -- [correctness/noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) -- [correctness/noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports) -- [correctness/noUnusedPrivateClassMembers](https://biomejs.dev/linter/rules/no-unused-private-class-members) -- [security/noGlobalEval](https://biomejs.dev/linter/rules/no-global-eval) -- [style/useConsistentArrayType](https://biomejs.dev/linter/rules/use-consistent-array-type) -- [style/useExportType](https://biomejs.dev/linter/rules/use-export-type) -- [style/useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) -- [style/useForOf](https://biomejs.dev/linter/rules/use-for-of) -- [style/useImportType](https://biomejs.dev/linter/rules/use-import-type) -- [style/useNodejsImportProtocol](https://biomejs.dev/linter/rules/use-nodejs-import-protocol) -- [style/useNumberNamespace](https://biomejs.dev/linter/rules/use-number-namespace) -- [style/useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type) -- [suspicious/noEmptyBlockStatements](https://biomejs.dev/linter/rules/no-empty-block-statements) -- [suspicious/noGlobalAssign](https://biomejs.dev/linter/rules/no-global-assign) -- [suspicious/noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class) -- [suspicious/noThenProperty](https://biomejs.dev/linter/rules/no-then-property) -- [suspicious/useAwait](https://biomejs.dev/linter/rules/use-await) - -Additionally, the following rules are now recommended: - -- [suspicious/noApproximativeNumericConstant](https://biomejs.dev/linter/rules/no-approximative-numeric-constant) -- [suspicious/noMisrefactoredShorthandAssign](https://biomejs.dev/linter/rules/no-misrefactored-shorthand-assign) - -#### Removed rules - -- Remove `nursery/useGroupedTypeImport`. The rule [style/useImportType](https://biomejs.dev/linter/rules/use-import-type) covers the behavior of this rule. - - Note that removing a nursery rule is not considered a breaking change according to our [semantic versioning](https://biomejs.dev/internals/versioning). - - Contributed by @Conaclos - -#### New features - -- Add the rule [noSkippedTests](https://biomejs.dev/linter/rules/no-skipped-tests), to disallow skipped tests: - - ```js - describe.skip("test", () => {}); - it.skip("test", () => {}); - ``` - Contributed by @ematipico - -- Add the rule [noFocusedTests](https://biomejs.dev/linter/rules/no-focused-tests), to disallow skipped tests: - - ```js - describe.only("test", () => {}); - it.only("test", () => {}); - ``` - Contributed by @ematipico - -- Add rule [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: - - ```diff - -
- +
- ``` - Contributed by @DaniGuardiola - -- Add rule [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies), to detect the use of - dependencies that aren't present in the `package.json`. - - The rule ignores imports using a protocol such as `node:`, `bun:`, `jsr:`, `https:`. - - Contributed by @ematipico and @Conaclos - -- Add rule [noNamespaceImport](https://biomejs.dev/linter/rules/no-namespace-import), to report namespace imports: - - ```js - import * as foo from "foo"; - ``` - Contributed by @unvalley -- Add partial support for `.astro` files. Biome is able to lint and fix the frontmatter of the Astro files. Contributed - by @ematipico - - ```diff - --- - - delete a.b - + a.b = undefined - --- - -
- ``` - -- Add partial support for `.vue` files. Biome is able to lint and fix the script block of the Vue files. - - ```diff - + ``` + + Contributed by @Conaclos + +- [useExportType](https://biomejs.dev/linter/rules/use-export-type/) no longer removes leading comments ([#2685](https://github.com/biomejs/biome/issues/2685)). + + Previously, `useExportType` removed leading comments when it factorized the `type` qualifier. + It now provides a code fix that preserves the leading comments: + + ```diff + - export { + + export type { + /**leading comment*/ + - type T + + T + } + ``` + + Contributed by @Conaclos + +- [useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable/) no longer reports false positive when iterating on non-jsx items ([#2590](https://github.com/biomejs/biome/issues/2590)). + + The following snipet of code no longer triggers the rule: + + ```jsx + <>{data.reduce((total, next) => total + next, 0)} + ``` + + Contributed by @dyc3 + +- Fix typo by renaming `useConsistentBuiltinInstatiation` to `useConsistentBuiltinInstantiation` + Contributed by @minht11 +- Fix the rule `useSingleCaseStatement` including `break` statements when counting the number of statements in a `switch` statement (#2696) + + +## 1.7.2 (2024-04-30) + +### Analyzer + +#### Bug fixes + +- Import sorting now ignores side effect imports ([#817](https://github.com/biomejs/biome/issues/817)). + + A side effect import consists now in its own group. + This ensures that side effect imports are not reordered. + + Here is an example of how imports are now sorted: + + ```diff + import "z" + - import { D } from "d"; + import { C } from "c"; + + import { D } from "d"; + import "y" + import "x" + - import { B } from "b"; + import { A } from "a"; + + import { B } from "b"; + import "w" + ``` + + Contributed by @Conaclos + +- Import sorting now adds spaces where needed ([#1665](https://github.com/biomejs/biome/issues/1665)) + Contributed by @Conaclos + +### CLI + +#### Bug fixes + +- `biome migrate eslint` now handles cyclic references. + + Some plugins and configurations export objects with cyclic references. + This causes `biome migrate eslint` to fail or ignore them. + These edge cases are now handled correctly. + + Contributed by @Conaclos + +### Formatter + +#### Bug fixes + +- Correctly handle placement of comments inside named import clauses. [#2566](https://github.com/biomejs/biome/pull/2566). Contributed by @ah-yu + +### Linter + +#### New features + +- Add [nursery/noReactSpecificProps](https://biomejs.dev/linter/rules/no-react-specific-props/). + Contributed by @marvin-j97 + +- Add [noUselessUndefinedInitialization](https://biomejs.dev/linter/rules/no-useless-undefined-initialization/). + Contributed by @lutaok + +- Add [nursery/useArrayLiterals](https://biomejs.dev/linter/rules/use-array-literals/). + Contributed by @Kazuhiro-Mimaki + +- Add [nursery/useConsistentBuiltinInstatiation](https://biomejs.dev/linter/rules/use-consistent-builtin-instantiation/). + Contributed by @minht11 + +- Add [nursery/useDefaultSwitchClause](https://biomejs.dev/linter/rules/use-default-switch-clause/). + Contributed by @michellocana + +#### Bug fixes + +- [noDuplicateJsonKeys](https://biomejs.dev/linter/rules/no-duplicate-json-keys/) no longer crashes when a JSON file contains an unterminated string ([#2357](https://github.com/biomejs/biome/issues/2357)). + Contributed by @Conaclos + +- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) now reports redeclarations of parameters in a functions body ([#2394](https://github.com/biomejs/biome/issues/2394)). + + The rule was unable to detect redeclarations of a parameter or a type parameter in the function body. + The following two redeclarations are now reported: + + ```ts + function f(a) { + type T = number; // redeclaration + const a = 0; // redeclaration + } + ``` + + Contributed by @Conaclos + +- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) no longer reports overloads in object types ([#2608](https://github.com/biomejs/biome/issues/2608)). + + The rule no longer report redeclarations in the following code: + + ```ts + type Overloads = { + ({ a }: { a: number }): number, + ({ a }: { a: string }): string, + }; + ``` + + Contributed by @Conaclos + +- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare/) now merge default function export declarations and types ([#2372](https://github.com/biomejs/biome/issues/2372)). + + The following code is no longer reported as a redeclaration: + + ```ts + interface Foo {} + export default function Foo() {} + ``` + + Contributed by @Conaclos + +- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) no longer reports variable-only and type-only exports ([#2637](https://github.com/biomejs/biome/issues/2637)). + Contributed by @Conaclos + +- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer crash Biome when encountering a malformed conditional type ([#1695](https://github.com/biomejs/biome/issues/1695)). + Contributed by @Conaclos + +- [useConst](https://biomejs.dev/linter/rules/use-const/) now ignores a variable that is read before its assignment. + + Previously, the rule reported the following example: + + ```js + let x; + x; // read + x = 0; // write + ``` + + It is now correctly ignored. + + Contributed by @Conaclos + +- [useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type/) now suggests correct code fixes when parentheses are required ([#2595](https://github.com/biomejs/biome/issues/2595)). + + Previously, the rule didn't add parentheses when they were needed. + It now adds parentheses when the function signature is inside an array, a union, or an intersection. + + ```diff + - type Union = { (): number } | string; + + type Union = (() => number) | string; + ``` + + Contributed by @Conaclos + +- [useTemplate](https://biomejs.dev/linter/rules/use-template/) now correctly escapes strings ([#2580](https://github.com/biomejs/biome/issues/2580)). + + Previously, the rule didn't correctly escape characters preceded by an escaped character. + + Contributed by @Conaclos + +- [noMisplacedAssertion](https://biomejs.dev/linter/rules/no-misplaced-assertion/) now allow these matchers + + - `expect.any()` + - `expect.anything()` + - `expect.closeTo` + - `expect.arrayContaining` + - `expect.objectContaining` + - `expect.stringContaining` + - `expect.stringMatching` + - `expect.extend` + - `expect.addEqualityTesters` + - `expect.addSnapshotSerializer` + + Contributed by @fujiyamaorange + +### Parser + +#### Bug fixes + +- The language parsers no longer panic on unterminated strings followed by a newline and a space ([#2606](https://github.com/biomejs/biome/issues/2606), [#2410](https://github.com/biomejs/biome/issues/2410)). + + The following example is now parsed without making Biome panics: + + ``` + " + " + ``` + + Contributed by @Conaclos + + +## 1.7.1 (2024-04-22) + +### Editors + +#### Bug fixes + +- Fix [#2403](https://github.com/biomejs/biome/issues/2403) by printing the errors in the client console. Contributed by @ematipico + +### Formatter + +#### Bug fixes + +- Add parentheses for the return expression that has leading multiline comments. [#2504](https://github.com/biomejs/biome/pull/2504). Contributed by @ah-yu + +- Correctly format dangling comments of continue statements. [#2555](https://github.com/biomejs/biome/pull/2555). Contributed by @ah-yu + +- Prevent comments from being eaten by the formatter [#2578](https://github.com/biomejs/biome/pull/2578). Now the comments won't be eaten for the following code: + ```js + console.log((a,b/* comment */)); + ``` + Contributed by @ah-yu + +- Correctly format nested union type to avoid reformatting issue. [#2628](https://github.com/biomejs/biome/pull/2628). Contributed by @ah-yu + +### Linter + +#### Bug fixes + +- Fix case where `jsxRuntime` wasn't being respected by `useImportType` rule ([#2473](https://github.com/biomejs/biome/issues/2473)).Contributed by @arendjr +- Fix [#2460](https://github.com/biomejs/biome/issues/2460), where the rule `noUselessFragments` was crashing the linter in some cases. Now cases like these are correctly handled: + ```jsx + callFunction(<>{bar}) + ``` + Contributed by @ematipico +- Fix [#2366](https://github.com/biomejs/biome/issues/2366), where `noDuplicateJsonKeys` incorrectly computed the kes to highlight. Contributed by @ematipico +#### Enhancements + +- The rule `noMisplacedAssertions` now considers valid calling `expect` inside `waitFor`: + ```js + import { waitFor } from '@testing-library/react'; + + await waitFor(() => { + expect(111).toBe(222); + }); + ``` + Contributed by @ematipico + + +## 1.7.0 (2024-04-15) + +### Analyzer + +#### Bug fixes + +- Now Biome can detect the script language in Svelte and Vue script blocks more reliably ([#2245](https://github.com/biomejs/biome/issues/2245)). Contributed by @Sec-ant + +- `useExhaustiveDependencies` no longer reports recursive calls as missing + dependencies ([#2361](https://github.com/biomejs/biome/issues/2361)). + Contributed by @arendjr + +- `useExhaustiveDependencies` correctly reports missing dependencies declared + using function declarations ([#2362](https://github.com/biomejs/biome/issues/2362)). + Contributed by @arendjr + +- Biome now can handle `.svelte` and `.vue` files with `CRLF` as the end-of-line sequence. Contributed by @Sec-ant + +- `noMisplacedAssertion` no longer reports method calls by `describe`, `test`, `it` objects (e.g. `test.each([])()`) ([#2443](https://github.com/biomejs/biome/issues/2443)). Contributed by @unvalley. + +- Biome now can handle `.vue` files with [generic components](https://vuejs.org/api/sfc-script-setup#generics) ([#2456](https://github.com/biomejs/biome/issues/2456)). + ```vue + + ``` + Contributed by @Sec-ant + +#### Enhancements + +- Complete the well-known file lists for JSON-like files. Trailing commas are allowed in `.jsonc` files by default. Some well-known files like `tsconfig.json` and `.babelrc` don't use the `.jsonc` extension but still allow comments and trailing commas. While others, such as `.eslintrc.json`, only allow comments. Biome is able to identify these files and adjusts the `json.parser.allowTrailingCommas` option accordingly to ensure they are correctly parsed. Contributed by @Sec-ant + +- Fix dedent logic inconsistent with prettier where the indent-style is space and the indent-width is not 2. Contributed by @mdm317 + +### CLI + +#### New features + +- Add a command to migrate from ESLint + + `biome migrate eslint` allows you to migrate an ESLint configuration to Biome. + The command supports [legacy ESLint configurations](https://eslint.org/docs/latest/use/configure/configuration-files) and [new flat ESLint configurations](https://eslint.org/docs/latest/use/configure/configuration-files-new). + Legacy ESLint configurations using the YAML format are not supported. + + When loading a legacy ESLint configuration, Biome resolves the `extends` field. + It resolves both shared configurations and plugin presets! + To do this, it invokes _Node.js_. + + Biome relies on the metadata of its rules to determine the [equivalent rule of an ESLint rule](https://biomejs.dev/linter/rules-sources/). + A Biome rule is either inspired or roughly identical to an ESLint rules. + By default, inspired and nursery rules are excluded from the migration. + You can use the CLI flags `--include-inspired` and `--include-nursery` to migrate them as well. + + Note that this is a best-effort approach. + You are not guaranteed to get the same behavior as ESLint. + + Given the following ESLint configuration: + + ```json + { + "ignore_patterns": ["**/*.test.js"], + "globals": { "var2": "readonly" }, + "rules": { + "eqeqeq": "error" + }, + "overrides": [{ + "files": ["lib/*.js"], + "rules": { + "default-param-last": "off" + } + }] + } + ``` + + `biome migrate eslint --write` changes the Biome configuration as follows: + + ```json + { + "linter": { + "rules": { + "recommended": false, + "suspicious": { + "noDoubleEquals": "error" + } + } + }, + "javascript": { "globals": ["var2"] }, + "overrides": [{ + "include": ["lib/*.js"], + "linter": { + "rules": { + "style": { + "useDefaultParameterLast": "off" + } + } + } + }] + } + ``` + + Also, if the working directory contains `.eslintignore`, then Biome migrates the glob patterns. + Nested `.eslintignore` in subdirectories and negated glob patterns are not supported. + + If you find any issue, please don't hesitate to report them. + + Contributed by @Conaclos + +- Added two new options to customise the emitted output of the CLI: `--reporter=json` and `--reporter=json-pretty`. With `--reporter=json`, the diagnostics and the + summary will be printed in the **terminal** in JSON format. With `--reporter=json-pretty`, you can print the same information, but formatted using the same options of your configuration. + + NOTE: the shape of the JSON is considered experimental, and the shape of the JSON might change in the future. + +
+ Example of output when running `biome format` command + ```json + { + "summary": { + "changed": 0, + "unchanged": 1, + "errors": 1, + "warnings": 0, + "skipped": 0, + "suggestedFixesSkipped": 0, + "diagnosticsNotPrinted": 0 + }, + "diagnostics": [ + { + "category": "format", + "severity": "error", + "description": "Formatter would have printed the following content:", + "message": [ + { + "elements": [], + "content": "Formatter would have printed the following content:" + } + ], + "advices": { + "advices": [ + { + "diff": { + "dictionary": " statement();\n", + "ops": [ + { "diffOp": { "delete": { "range": [0, 2] } } }, + { "diffOp": { "equal": { "range": [2, 12] } } }, + { "diffOp": { "delete": { "range": [0, 2] } } }, + { "diffOp": { "equal": { "range": [12, 13] } } }, + { "diffOp": { "delete": { "range": [0, 2] } } }, + { "diffOp": { "insert": { "range": [13, 15] } } } + ] + } + } + ] + }, + "verboseAdvices": { "advices": [] }, + "location": { + "path": { "file": "format.js" }, + "span": null, + "sourceCode": null + }, + "tags": [], + "source": null + } + ], + "command": "format" + } + ``` +
+ +- Added new `--staged` flag to the `check`, `format` and `lint` subcommands. + + This new option allows users to apply the command _only_ to the files that are staged (the + ones that will be committed), which can be very useful to simplify writing git hook scripts + such as `pre-commit`. Contributed by @castarco + +#### Enhancements + +- Improve support of `.prettierignore` when migrating from Prettier + + Now, Biome translates most of the glob patterns in `.prettierignore` to the equivalent Biome ignore pattern. + Only negated glob patterns are not supported. + + Contributed by @Conaclos + +- Support JavaScript configuration files when migrating from Prettier + + `biome migrate prettier` is now able to migrate Prettier configuration files + ending with `js`, `mjs`, or `cjs` extensions. + To do this, Biome invokes Node.js. + + Also, embedded Prettier configurations in `package.json` are now supported. + + Contributed by @Conaclos + +- Support `overrides` field in Prettier configuration files when migrating from Prettier. + Contributed by @Conaclos + +- Support passing a file path to the `--config-path` flag or the `BIOME_CONFIG_PATH` environment variable. + + Now you can pass a `.json`/`.jsonc` file path with any filename to the `--config-path` flag or the + `BIOME_CONFIG_PATH` environment variable. This will disable the configuration auto-resolution and Biome + will try to read the configuration from the said file path ([#2265](https://github.com/biomejs/biome/issues/2265)). + + ```shell + biome format --config-path=../biome.json ./src + ``` + + Contributed by @Sec-ant + +#### Bug fixes + +- Biome now tags the diagnostics emitted by `organizeImports` and `formatter` with correct severity levels, so they will be properly filtered by the flag `--diagnostic-level` ([#2288](https://github.com/biomejs/biome/issues/2288)). Contributed by @Sec-ant + +- Biome now correctly filters out files that are not present in the current directory when using the `--changed` flag [#1996](https://github.com/biomejs/biome/issues/1996). Contributed by @castarco + +- Biome now skips traversing `fifo` or `socket` files ([#2311](https://github.com/biomejs/biome/issues/2311)). Contributed by @Sec-ant + +- Biome now resolves configuration files exported from external libraries in `extends` from the working directory (CLI) or project root (LSP). This is the documented behavior and previous resolution behavior is considered as a bug ([#2231](https://github.com/biomejs/biome/issues/2231)). Contributed by @Sec-ant + +### Configuration + +#### Bug fixes + +- Now setting group level `all` to `false` can disable recommended rules from that group when top level `recommended` is `true` or unset. Contributed by @Sec-ant + +- Biome configuration files can correctly extends `.jsonc` configuration files now ([#2279](https://github.com/biomejs/biome/issues/2279)). Contributed by @Sec-ant + +- Fixed the JSON schema for React hooks configuration ([#2396](https://github.com/biomejs/biome/issues/2396)). Contributed by @arendjr + +#### Enhancements + +- Biome now displays the location of a parsing error for its configuration file ([#1627](https://github.com/biomejs/biome/issues/1627)). + + Previously, when Biome encountered a parsing error in its configuration file, + it didn't indicate the location of the error. + It now displays the name of the configuration file and the range where the error occurred. + + Contributed by @Conaclos + +- `options` is no longer required for rules without any options ([#2313](https://github.com/biomejs/biome/issues/2313)). + + Previously, the JSON schema required to set `options` to `null` when an object is used to set the diagnostic level of a rule without any option. + However, if `options` is set to `null`, Biome emits an error. + + The schema is now fixed and it no longer requires specifying `options`. + This makes the following configuration valid: + + ```json + { + "linter": { + "rules": { + "style": { + "noDefaultExport": { + "level": "off" + } + } + } + } + } + ``` + + Contributed by @Conaclos + +### Editors + +#### Bug fixes + +- Biome extension is now able to parse the JSX syntax in files that associated with the `javascript` [language identifier](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem). This is an ad hoc fix, because [in the React world, `.js` files are allowed to include JSX syntax](https://github.com/facebook/create-react-app/issues/87#issuecomment-234627904), and these files are often associated with the `javascript` language identifier in most of the editors. Plus, [some editor extensions](https://github.com/michaelgmcd/vscode-language-babel/blob/8b3a472748ad07c99dc022b66795c9eb46be4ccb/package.json#L63-L80) will also associate `.jsx` files with the `javascript` language identifier. Relative links: [discussion](https://github.com/biomejs/biome/discussions/838#discussioncomment-9047539), [#2085](https://github.com/biomejs/biome/issues/2085). Contributed by @Sec-ant + +### Formatter + +#### Bug fixes + +- Fix [#2291](https://github.com/biomejs/biome/issues/2291) by correctly handle comment placement for JSX spread attributes and JSX spread children. Contributed by @ah-yu + +### JavaScript APIs + +### Linter + +#### Promoted rules + +New rules are incubated in the nursery group. +Once stable, we promote them to a stable group. +The following rules are promoted: + +- [complecity/noExcessiveNestedTestSuites](https://biomejs.dev/linter/rules/no-excessive-nested-test-suites) +- [complexity/noUselessTernary](https://biomejs.dev/linter/rules/no-useless-ternary) +- [correctness/useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable) +- [performance/noBarrelFile](https://biomejs.dev/linter/rules/no-barrel-file/) +- [performance/noReExportAll](https://biomejs.dev/linter/rules/no-re-export-all/) +- [style/noNamespaceImport](https://biomejs.dev/linter/rules/no-namespace-import/) +- [style/useNodeAssertStrict](https://biomejs.dev/linter/rules/use-node-assert-strict/) +- [suspicious/noDuplicateTestHooks](https://biomejs.dev/linter/rules/no-duplicate-test-hooks/) +- [suspicious/noExportsInTest](https://biomejs.dev/linter/rules/no-exports-in-test/) +- [suspicious/noFocusedTests](https://biomejs.dev/linter/rules/no-focused-tests/) +- [suspicious/noSkippedTests](https://biomejs.dev/linter/rules/no-skipped-tests/) +- [suspicious/noSuspiciousSemicolonInJsx](https://biomejs.dev/linter/rules/no-suspicious-semicolon-in-jsx) + +#### New features + +- Add a new option `jsxRuntime` to the `javascript` configuration. When set to `reactClassic`, the [noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports) and [useImportType](https://biomejs.dev/linter/rules/use-import-type) rules use this information to make exceptions for the React global that is required by the React Classic JSX transform. + + This is only necessary for React users who haven't upgraded to the [new JSX transform](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). + + Contributed by @Conaclos and @arendjr + +- Implement [#2043](https://github.com/biomejs/biome/issues/2043): The React rule [`useExhaustiveDependencies`](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/) is now also compatible with Preact hooks imported from `preact/hooks` or `preact/compat`. Contributed by @arendjr + +- Add rule [noFlatMapIdentity](https://biomejs.dev/linter/rules/no-flat-map-identity) to disallow unnecessary callback use on `flatMap`. Contributed by @isnakode + +- Add rule [noConstantMathMinMaxClamp](https://biomejs.dev/linter/rules/no-constant-math-min-max-clamp), which disallows using `Math.min` and `Math.max` to clamp a value where the result itself is constant. Contributed by @mgomulak + +#### Enhancements + +- [style/useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention/) now allows prefixing a filename with `+` ([#2341](https://github.com/biomejs/biome/issues/2341)). + + This is a convention used by [Sveltekit](https://kit.svelte.dev/docs/routing#page) and [Vike](https://vike.dev/route). + + Contributed by @Conaclos + +- [style/useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now accepts `PascalCase` for local and top-level variables. + + This allows supporting local variables that hold a component or a regular class. + The following code is now accepted: + + ```tsx + function loadComponent() { + const Component = getComponent(); + return ; + } + ``` + + Contributed by @Conaclos + +- [complexity/useLiteralKeys](https://biomejs.dev/linter/rules/use-literal-keys/) no longer report computed properties named `__proto__` ([#2430](https://github.com/biomejs/biome/issues/2430)). + + In JavaScript, `{["__proto__"]: null}` and `{__proto__: null}` have not the same semantic. + The first code set a regular property to `null`. + The second one set the prototype of the object to `null`. + See the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) for more details. + + The rule now ignores computed properties named `__proto__`. + + Contributed by @Conaclos + +#### Bug fixes + +- Lint rules `useNodejsImportProtocol`, `useNodeAssertStrict`, `noRestrictedImports`, `noNodejsModules` will no longer check `declare module` statements anymore. Contributed by @Sec-ant + +- [style/useNamingConvention](https://biomejs.dev/linter/rules/use-naming-convention/) now accepts any case for variables from object destructuring ([#2332](https://github.com/biomejs/biome/issues/2332)). + + The following name is now ignored: + + ```js + const { Strange_Style } = obj; + ``` + + Previously, the rule renamed this variable. This led to a runtime error. + + Contributed by @Conaclos + +### Parser + +#### Bug fixes + +- Fixed an issue when Unicode surrogate pairs were encoded in JavaScript strings + using an escape sequence ([#2384](https://github.com/biomejs/biome/issues/2384)). + Contributed by @arendjr + + +## 1.6.4 (2024-04-03) + +### Analyzer + +#### Bug fixes + +- An operator with no spaces around in a binary expression no longer breaks the js analyzer ([#2243](https://github.com/biomejs/biome/issues/2243)). Contributed by @Sec-ant + +### CLI + +#### Bug fixes + +- Fix the printed error count ([#2048](https://github.com/biomejs/biome/issues/2048)). Contributed by @Sec-ant + +### Configuration + +#### Bug fixes + +- Correctly calculate enabled rules in lint rule groups. Now a specific rule belonging to a group can be enabled even if its group-level preset option `recommended` or `all` is `false` ([#2191](https://github.com/biomejs/biome/issues/2191)). Contributed by @Sec-ant + +### Editors + +#### Bug fixes + +- Fix the unexpected code deletion and repetition when `quickfix.biome` is enabled and some `import`-related rules are applied ([#2222](https://github.com/biomejs/biome/issues/2222), [#688](https://github.com/biomejs/biome/issues/688), [#1015](https://github.com/biomejs/biome/issues/1015)). Contributed by @Sec-ant + +### Linter + +#### New features + +- Add [nursery/noMisplacedAssertion](https://biomejs.dev/linter/rules/no-misplaced-assertion/). COntributed by @ematipico + +#### Bug fixes + +- Fix [#2211](https://github.com/biomejs/biome/issues/2211). noChildrenProp should work fine when children pass as a prop in a new line. Contributed by @fireairforce + +- Fix [#2248](https://github.com/biomejs/biome/issues/2248). `lint/a11y/useButtonType` should not trigger when button element with spread attribute. Contributed by @fireairforce + +- Fix [#2216](https://github.com/biomejs/biome/issues/2216). `lint/style/useNamingConvention` should not ignore JSX Component name binding. Contributed by @fireairforce + +#### Enhancements + +- Add support for object property members in the rule `useSortedClasses`. Contributed by @ematipico + +### Parser + +- The parser doesn't throw any error when the frontmatter of `.astro` files contains an illegal return: + + ```astro + --- + const condition = true; + if (condition) { + return "Something"; + } + --- +
+ ``` + Contributed by @ematipico + +## 1.6.3 (2024-03-25) + +### CLI + +#### Bug fixes + +- Fix configuration resolution. Biome is now able to correctly find the `biome.jsonc` configuration file when `--config-path` is explicitly set ([#2164](https://github.com/biomejs/biome/issues/2164)). Contributed by @Sec-ant + +- JavaScript/TypeScript files of different variants (`.ts`, `.js`, `.tsx`, `.jsx`) in a single workspace now have stable formatting behaviors when running the CLI command in paths of different nested levels or in different operating systems ([#2080](https://github.com/biomejs/biome/issues/2080), [#2109](https://github.com/biomejs/biome/issues/2109)). Contributed by @Sec-ant + +### Configuration + +#### Bug fixes + +- Complete the documentation and overrides support for options `formatter.lineEnding`, `[language].formatter.lineEnding`, `formatter.attributePosition` and `javascript.formatter.attributePosition`. Contributed by @Sec-ant + +### Formatter + +#### Bug fixes + +- Fix [#2172](https://github.com/biomejs/biome/issues/2172) by breaking long object destructuring patterns. Contributed by @ah-yu + +### Linter + +#### New features + +- Add rule [noEvolvingTypes](https://biomejs.dev/linter/rules/no-evolving-any) to disallow variables from evolving into `any` type through reassignments. Contributed by @fujiyamaorange + +#### Enhancements + +- Rename `noSemicolonInJsx` to `noSuspiciousSemicolonInJsx`. Contributed by @fujiyamaorange + +### LSP + +#### Bug fixes + +- Quickfix action no longer autofixes lint rule errors on save when `linter` is disabled ([#2161](https://github.com/biomejs/biome/issues/2161)). Contributed by @Sec-ant +- Range formatting for Astro/Svelte/Vue doesn't place code out of place, especially when formatting on paste is enabled. Contributed by @ematipico + +## 1.6.2 (2024-03-22) + +### Analyzer + +#### Bug fixes + +- The `noSuperWithoutExtends` rule now allows for calling `super()` in derived class constructors of class expressions ([#2108](https://github.com/biomejs/biome/issues/2108)). Contributed by @Sec-ant + +- Fix discrepancies on file source detection. Allow module syntax in `.cts` files ([#2114](https://github.com/biomejs/biome/issues/2114)). Contributed by @Sec-ant + +### CLI + +#### Bug fixes + +- Fixes [#2131](https://github.com/biomejs/biome/issues/2131), where folders were incorrectly ignored when running the command `check`. Now folders are correctly ignored based on their command. Contributed by @ematipico + +- Smoother handling of `"endOfLine": "auto"` in prettier migration: falling back to `"lf"` ([#2145](https://github.com/biomejs/biome/pull/2145)). Contributed by @eMerzh + +### Configuration + +#### Bug fixes + +- Fix enabled rules calculation. The precendence of individual rules, `all` and `recommend` presets in top-level and group-level configs is now correctly respected. More details can be seen in ([#2072](https://github.com/biomejs/biome/pull/2072)) ([#2028](https://github.com/biomejs/biome/issues/2028)). Contributed by @Sec-ant + +### Formatter + +#### Bug fixes + +- Fix [#1661](https://github.com/biomejs/biome/issues/1661). Now nested conditionals are aligned with Prettier's logic, and won't contain mixed spaces and tabs. Contributed by @ematipico + +### JavaScript APIs + +#### Enhancements + +- Support applying lint fixes when calling the `lintContent` method of the `Biome` class ([#1956](https://github.com/biomejs/biome/pull/1956)). Contributed by @mnahkies + +### Linter + +#### New features + +- Add [nursery/noDuplicateElseIf](https://biomejs.dev/linter/rules/no-duplicate-else-if/). COntributed by @mdm317 + +#### Bug fixes + +- Rule `noUndeclaredDependencies` now also validates `peerDependencies` and `optionalDependencies` ([#2122](https://github.com/biomejs/biome/issues/2122)). Contributed by @Sec-ant + +- Rule `noUndeclaredDependencies` won't check `declare module` statements anymore ([#2123](https://github.com/biomejs/biome/issues/2123)). Contributed by @Sec-ant + +- Fix [#1925](https://github.com/biomejs/biome/issues/1925). The fix for `useOptionalChain` would sometimes suggest an incorrect fix that discarded optional chaining operators on the left-hand side of logical expressions. These are now preserved. Contributed by @arendjr + +- Rule `noUndeclaredVariables` now also checks for worker globals ([#2121](https://github.com/biomejs/biome/issues/2121)). Contributed by @Sec-ant + +### LSP + +#### Bug fixes + +- Correctly parse `.jsonc` files. Contributed by @Sec-ant + +- Correctly resolve external `extends` configs. Contributed by @Sec-ant + +## 1.6.1 (2024-03-12) + +### CLI + +#### Bug fixes + +- CLI is now able to automatically search and resolve `biome.jsonc` ([#2008](https://github.com/biomejs/biome/issues/2008)). Contributed by @Sec-ant +- Fix a false positive where some files were counted as "fixed" even though they weren't modified. Contributed by @ematipico + +### Configuration + +#### Bug fixes + +- `json.formatter.trailingCommas` option now works in `overrides` ([#2009](https://github.com/biomejs/biome/issues/2009)). Contributed by @Sec-ant + +### Linter + +#### New features + +- Add rule [noDoneCallback](https://biomejs.dev/linter/rules/no-done-callback), this rule checks the function parameter of hooks & tests + for use of the done argument, suggesting you return a promise instead. Contributed by @vasucp1207 + + ```js + beforeEach(done => { + // ... + }); + ``` + +#### Bug fixes + +- [useJsxKeyInIterable](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable) now recognizes function bodies wrapped in parentheses ([#2011](https://github.com/biomejs/biome/issues/2011)). Contributed by @Sec-ant + +- [useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type) now preserves type parameters of generic interfaces when applying fixes ([#2015](https://github.com/biomejs/biome/issues/2015)). Contributed by @Sec-ant + +- Code fixes of [useImportType](https://biomejs.dev/linter/rules/use-import-type) and [useExportType](https://biomejs.dev/linter/rules/use-export-type) now handle multiline statements ([#2041](https://github.com/biomejs/biome/issues/2041)). Contributed by @Conaclos + +- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare) no longer reports type parameter and parameter with identical names ([#1992](https://github.com/biomejs/biome/issues/1992)). + + The following code is no longer reported: + + ```ts + function f
(a: a) {} + ``` + + Contributed by @Conaclos + +- [noRedeclare](https://biomejs.dev/linter/rules/no-redeclare) now reports duplicate type parameters in a same declaration. + + The following type parameters are now reported as a redeclaration: + + ```ts + function f() {} + ``` + + Contributed by @Conaclos + +- [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) now recognizes imports of subpath exports. + + E.g., the following import statements no longer report errors if `@mui/material` and `tailwindcss` are installed as dependencies: + + ```ts + import Button from "@mui/material/Button"; + import { fontFamily } from "tailwindcss/defaultTheme"; + ``` + + Contributed by @Sec-ant + +### Parser + +#### Bug fixes + +- JavaScript lexer is now able to lex regular expression literals with escaped non-ascii chars ([#1941](https://github.com/biomejs/biome/issues/1941)). + + Contributed by @Sec-ant + +## 1.6.0 (2024-03-08) + +### Analyzer + +#### New features + +- Add partial for `.astro` files. Biome is able to sort imports inside the frontmatter of the Astro files. Contributed + by @ematipico + + ```diff + --- + - import { getLocale } from "astro:i18n"; + - import { Code } from "astro:components"; + + import { Code } from "astro:components"; + + import { getLocale } from "astro:i18n"; + --- + +
+ ``` +- Add partial for `.vue` files. Biome is able to sort imports inside the script block of Vue files. Contributed by + @nhedger + + ```diff + + + + ``` + +- Add partial for `.svelte` files. Biome is able to sort imports inside the script block of Svelte files. Contributed by + @ematipico + + ```diff + + +
+ ``` + +- The analyzer now **infers** the correct quote from `javascript.formatter.quoteStyle`, if set. This means that code fixes suggested by the analyzer will use the same quote of the formatter. Contributed by @ematipico + +#### Enhancements + +- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables) ignores unused rest spread siblings. + + The following code is now valid: + + ```js + const { a, ...rest } = { a: 0, b: 1 }; + console.log(rest); + ``` + + Contributed by @ah-yu + +- Fix [#1931](https://github.com/biomejs/biome/issues/1931). Built-in React hooks such as + `useEffect()` can now be validated by the + [`useExhaustiveDependendies`](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/), even + when they're not being imported from the React library. To do so, simply configure them like + any other user-provided hooks. + + Contributed by @arendjr + +- Implemented [#1128](https://github.com/biomejs/biome/issues/1128). User-provided React hooks can + now be configured to track stable results. For example: + + ```json + "useExhaustiveDependencies": { + "level": "error", + "options": { + "hooks": [{ + "name": "useMyState", + "stableResult": [ + 1 + ] + }] + } + } + ``` + + This will allow the following to be validated: + + ```js + const [myState, setMyState] = useMyState(); + const toggleMyState = useCallback(() => { + setMyState(!myState); + }, [myState]); // Only `myState` needs to be specified here. + ``` + + Contributed by @arendjr + +#### Bug fixes + +- Fix [#1748](https://github.com/biomejs/biome/issues/1748). Now for the following case we won't provide an unsafe fix + for the `noNonNullAssertion` rule: + + ```ts + x[y.z!]; + ``` + + Contributed by @ah-yu + +- Imports that contain the protocol `:` are now sorted after the `npm:` modules, and before the `URL` modules. + Contributed by @ematipico + + ```diff + import express from "npm:express"; + - import Component from "./component.js" + - import { sortBy } from "virtual:utils"; + + import { sortBy } from "virtual:utils"; + + import Component from "./component.js" + ``` + +- Fix [#1081](https://github.com/biomejs/biome/issues/1081). The `useAwait` rule does not report `for await...of`. + Contributed by @unvalley + +- Fix [#1827](https://github.com/biomejs/biome/issues/1827) by properly analyzing nested `try-finally` statements. Contributed by @ah-yu + +- Fix [#1924](https://github.com/biomejs/biome/issues/1924) Use the correct export name to sort in the import clause. Contributed by @ah-yu +- Fix [#1805](https://github.com/biomejs/biome/issues/1805) fix formatting arrow function which has conditional expression body Contributed by @mdm317 + +- Fix [#1781](https://github.com/biomejs/biome/issues/1781) by avoiding the retrieval of the entire static member expression for the reference if the static member expression does not start with the reference. Contributed by @ah-yu + +### CLI + +#### New features + +- Add a new command `biome migrate prettier`. The command will read the file `.prettierrc`/`prettier.json` + and `.prettierignore` and map its configuration to Biome's one. + Due to the different nature of `.prettierignore` globs and Biome's globs, it's **highly** advised to make sure that + those still work under Biome. + +- Now the file name printed in the diagnostics is clickable. If you run the CLI from your editor, you can + Ctrl/ + Click on the file name, and the editor will open said file. If row and columns + are specified e.g. `file.js:32:7`, the editor will set the cursor right in that position. Contributed by @ematipico + +- Add an option `--linter` to `biome rage`. The option needs to check Biome linter configuration. Contributed by + @seitarof + +- Add an option `--formatter` to `biome rage`. The option needs to check Biome formatter configuration. Contributed by + @seitarof +- The CLI now consistently reports the number of files tha were changed, out of the total files that were analysed. Contributed by @ematipico +- The CLI now consistently shows the number of errors and warnings emitted. Contributed by @ematipico + +#### Bug fixes + +- Don't process files under an ignored directory. + + Previously, Biome processed all files in the traversed hierarchy, + even the files under an ignored directory. + Now, it completely skips the content of ignored directories. + + For now, directories cannot be ignored using `files.include` in the configuration file. + This is a known limitation that we want to address in a future release. + + For instance, if you have a project with a folder `src` and a folder `test`, + the following configuration doesn't completely ignore `test`. + + ```json + { + "files": { + "include": ["src"] + } + } + ``` + + Biome will traverse `test`, + however all files of the directory are correctly ignored. + This can result in file system errors, + if Biome encounters dangling symbolic links or files with higher permissions. + + To avoid traversing the `test` directory, + you should ignore the directory using `ignore`: + + ```json + { + "files": { + "include": ["src"], + "ignore": ["test"] + } + } + ``` + +- Fix [#1508](https://github.com/biomejs/biome/issues/1508) by excluding deleted files from being processed. Contributed + by @ematipico + +- Fix [#1173](https://github.com/biomejs/biome/issues/1173). Fix the formatting of a single instruction with commented + in a control flow body to ensure consistency. Contributed by @mdm317 + +- Fix overriding of `javascript.globals`. Contributed by @arendjr +- Fix a bug where syntax rules weren't run when pulling the diagnostics. Now Biome will emit more parsing diagnostics, + e.g. + ``` + check.js:1:17 parse/noDuplicatePrivateClassMembers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Duplicate private class member "#foo" + + > 1 │ class A { #foo; #foo } + │ ^^^^ + + ``` + Contributed by @ematipico + +- Fix [#1774](https://github.com/biomejs/biome/issues/1774) by taking into account the option `--no-errors-on-unmatched` when running the CLI using `--changed`. Contributed by @antogyn + +#### Enhancements + +- Removed a superfluous diagnostic that was printed during the linting/check phase of a file: + + ``` + test.js check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × The file contains diagnostics that needs to be addressed. + ``` + Contributed by @ematipico +- The command `format` now emits parsing diagnostics if there are any, and it will terminate with a non-zero exit code. Contributed by @ematipico + +### Configuration + +#### New features + +- Add the ability to resolve the configuration files defined inside `extends` from the `node_modules/` directory. + + If you want to resolve a configuration file that matches the specifier `@org/configs/biome`, then your `package.json` + file must look this: + + ```json + { + "name": "@org/configs", + "exports": { + "./biome": "./biome.json" + } + } + ``` + + And the `biome.json` file that "imports" said configuration, will look like this: + ```json + { + "extends": "@org/configs/biome" + } + ``` + Read the [documentation](https://biomejs.dev/guides/how-biome-works#the-extends-option) to better understand how it + works, expectations and restrictions. + +### Editors + +#### Bug fixes + +- Fix a regression where ignored files where formatted in the editor. Contributed by @ematipico +- Fix a bug where syntax rules weren't run when pulling the diagnostics. Now Biome will emit more parsing diagnostics, + e.g. + ``` + check.js:1:17 parse/noDuplicatePrivateClassMembers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Duplicate private class member "#foo" + + > 1 │ class A { #foo; #foo } + │ ^^^^ + + ``` + Contributed by @ematipico + +### Formatter + +#### New features + +- Biome now allows to format the `package.json` file. This is now the default behaviour and users can remove their + workarounds. + If you rely on other tools to format `package.json`, you'll have to ignore it via configuration. Contributed by + @pattrickrice +- New formatter option `attributePosition` that have similar behavior as + Prettier `singleAttributePerLine` [#1706](https://github.com/biomejs/biome/issues/1706). Contributed by @octoshikari +- Add partial for `.astro` files. Biome is able to format the frontmatter of the Astro files. Contributed by @ematipico + + ```diff + --- + - statement ( ); + + statement(); + --- + +
+ ``` +- Add partial for `.vue` files. Biome is able to format the script block of Vue files. Contributed by @nhedger + + ```diff + + + + ``` + +- Add partial for `.svelte` files. Biome is able to format the script block of Svelte files. Contributed by @ematipico + + ```diff + + +
+ ``` + +#### Enhancements + +- `composer.json`, `deno.json`, `jsconfig.json`, `package.json` and `tsconfig.json` are no longer protected files. + + This means that you can now format them. + + If you want to ignore these files, you can use the [files.ignore](https://biomejs.dev/reference/configuration/#filesignore) configuration: + + ```json + { + "files": { + "ignore": [ + "composer.json", + "jsconfig.json", + "package.json", + "tsconfig.json", + "typescript.json", + "deno.json", + "deno.jsonc" + ] + } + } + ``` + + The following files are still protected, and thus ignored: + + - `composer.lock` + - `npm-shrinkwrap.json` + - `package-lock.json` + - `yarn.lock` + + Contributed by @pattrickrice and @Conaclos + +#### Bug fixes + +- Fix [#1039](https://github.com/biomejs/biome/issues/1039). Check unicode width instead of number of bytes when + checking if regex expression is a simple argument. + + This no longer breaks. + + ```js + s(/🚀🚀/).s().s(); + ``` + + Contributed by @kalleep + +- Fix [#1218](https://github.com/biomejs/biome/issues/1218), by correctly preserving empty lines in member chains. + Contributed by @ah-yu +- Fix [#1659](https://github.com/biomejs/biome/issues/1659) and [#1662](https://github.com/biomejs/biome/issues/1662), by correctly taking into account the leading comma inside the formatter options. Contributed by @ematipico + +- Fix [#1934](https://github.com/biomejs/biome/pull/1934). Fix invalid formatting of long arrow function for AsNeeded arrow parens Contributed by @fireairforce + +### JavaScript APIs + +### Linter + +#### Promoted rules + +New rules are incubated in the nursery group. +Once stable, we promote them to a stable group. +The following rules are promoted: + +- [complexity/noEmptyTypeParameters](https://biomejs.dev/linter/rules/no-empty-type-parameters) +- [complexity/noUselessLoneBlockStatements](https://biomejs.dev/linter/rules/no-useless-lone-block-statements) +- [correctness/noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) +- [correctness/noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports) +- [correctness/noUnusedPrivateClassMembers](https://biomejs.dev/linter/rules/no-unused-private-class-members) +- [security/noGlobalEval](https://biomejs.dev/linter/rules/no-global-eval) +- [style/useConsistentArrayType](https://biomejs.dev/linter/rules/use-consistent-array-type) +- [style/useExportType](https://biomejs.dev/linter/rules/use-export-type) +- [style/useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) +- [style/useForOf](https://biomejs.dev/linter/rules/use-for-of) +- [style/useImportType](https://biomejs.dev/linter/rules/use-import-type) +- [style/useNodejsImportProtocol](https://biomejs.dev/linter/rules/use-nodejs-import-protocol) +- [style/useNumberNamespace](https://biomejs.dev/linter/rules/use-number-namespace) +- [style/useShorthandFunctionType](https://biomejs.dev/linter/rules/use-shorthand-function-type) +- [suspicious/noEmptyBlockStatements](https://biomejs.dev/linter/rules/no-empty-block-statements) +- [suspicious/noGlobalAssign](https://biomejs.dev/linter/rules/no-global-assign) +- [suspicious/noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class) +- [suspicious/noThenProperty](https://biomejs.dev/linter/rules/no-then-property) +- [suspicious/useAwait](https://biomejs.dev/linter/rules/use-await) + +Additionally, the following rules are now recommended: + +- [suspicious/noApproximativeNumericConstant](https://biomejs.dev/linter/rules/no-approximative-numeric-constant) +- [suspicious/noMisrefactoredShorthandAssign](https://biomejs.dev/linter/rules/no-misrefactored-shorthand-assign) + +#### Removed rules + +- Remove `nursery/useGroupedTypeImport`. The rule [style/useImportType](https://biomejs.dev/linter/rules/use-import-type) covers the behavior of this rule. + + Note that removing a nursery rule is not considered a breaking change according to our [semantic versioning](https://biomejs.dev/internals/versioning). + + Contributed by @Conaclos + +#### New features + +- Add the rule [noSkippedTests](https://biomejs.dev/linter/rules/no-skipped-tests), to disallow skipped tests: + + ```js + describe.skip("test", () => {}); + it.skip("test", () => {}); + ``` + Contributed by @ematipico + +- Add the rule [noFocusedTests](https://biomejs.dev/linter/rules/no-focused-tests), to disallow skipped tests: + + ```js + describe.only("test", () => {}); + it.only("test", () => {}); + ``` + Contributed by @ematipico + +- Add rule [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: + + ```diff + -
+ +
+ ``` + Contributed by @DaniGuardiola + +- Add rule [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies), to detect the use of + dependencies that aren't present in the `package.json`. + + The rule ignores imports using a protocol such as `node:`, `bun:`, `jsr:`, `https:`. + + Contributed by @ematipico and @Conaclos + +- Add rule [noNamespaceImport](https://biomejs.dev/linter/rules/no-namespace-import), to report namespace imports: + + ```js + import * as foo from "foo"; + ``` + Contributed by @unvalley +- Add partial support for `.astro` files. Biome is able to lint and fix the frontmatter of the Astro files. Contributed + by @ematipico + + ```diff + --- + - delete a.b + + a.b = undefined + --- + +
+ ``` + +- Add partial support for `.vue` files. Biome is able to lint and fix the script block of the Vue files. + + ```diff +
"#; -const SVELTE_TS_FILE_LINT_APPLY_AFTER: &str = r#" -
"#; - -const SVELTE_TS_FILE_LINT_APPLY_UNSAFE_AFTER: &str = r#" -
"#; - const SVELTE_TS_FILE_CHECK_BEFORE: &str = r#"
"#; -const SVELTE_TS_FILE_CHECK_APPLY_AFTER: &str = r#" -
"#; - -const SVELTE_TS_FILE_CHECK_APPLY_UNSAFE_AFTER: &str = r#" -
"#; - #[test] fn sorts_imports_check() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let svelte_file_path = Path::new("file.svelte"); + let svelte_file_path = Utf8Path::new("file.svelte"); fs.insert( svelte_file_path.into(), SVELTE_FILE_IMPORTS_BEFORE.as_bytes(), ); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from( [ - ("check"), + "check", "--formatter-enabled=false", "--linter-enabled=false", - svelte_file_path.as_os_str().to_str().unwrap(), + svelte_file_path.as_str(), ] .as_slice(), ), @@ -119,22 +89,22 @@ fn sorts_imports_write() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let svelte_file_path = Path::new("file.svelte"); + let svelte_file_path = Utf8Path::new("file.svelte"); fs.insert( svelte_file_path.into(), SVELTE_FILE_IMPORTS_BEFORE.as_bytes(), ); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from( [ - ("check"), + "check", "--formatter-enabled=false", "--linter-enabled=false", - "--apply", - svelte_file_path.as_os_str().to_str().unwrap(), + "--write", + svelte_file_path.as_str(), ] .as_slice(), ), @@ -158,16 +128,16 @@ fn format_svelte_ts_context_module_files() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let svelte_file_path = Path::new("file.svelte"); + let svelte_file_path = Utf8Path::new("file.svelte"); fs.insert( svelte_file_path.into(), SVELTE_TS_CONTEXT_MODULE_FILE_UNFORMATTED.as_bytes(), ); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, - Args::from([("format"), svelte_file_path.as_os_str().to_str().unwrap()].as_slice()), + Args::from(["format", svelte_file_path.as_str()].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -192,23 +162,16 @@ fn format_svelte_ts_context_module_files_write() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let svelte_file_path = Path::new("file.svelte"); + let svelte_file_path = Utf8Path::new("file.svelte"); fs.insert( svelte_file_path.into(), SVELTE_TS_CONTEXT_MODULE_FILE_UNFORMATTED.as_bytes(), ); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, - Args::from( - [ - "format", - "--write", - svelte_file_path.as_os_str().to_str().unwrap(), - ] - .as_slice(), - ), + Args::from(["format", "--write", svelte_file_path.as_str()].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -233,16 +196,16 @@ fn format_svelte_carriage_return_line_feed_files() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let svelte_file_path = Path::new("file.svelte"); + let svelte_file_path = Utf8Path::new("file.svelte"); fs.insert( svelte_file_path.into(), SVELTE_CARRIAGE_RETURN_LINE_FEED_FILE_UNFORMATTED.as_bytes(), ); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, - Args::from([("format"), svelte_file_path.as_os_str().to_str().unwrap()].as_slice()), + Args::from(["format", svelte_file_path.as_str()].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -264,32 +227,21 @@ fn format_svelte_carriage_return_line_feed_files() { #[test] fn format_stdin_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_CONTEXT_MODULE_FILE_UNFORMATTED.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["format", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_CONTEXT_MODULE_FILE_FORMATTED); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "format_stdin_successfully", @@ -301,32 +253,21 @@ fn format_stdin_successfully() { #[test] fn format_stdin_write_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_CONTEXT_MODULE_FILE_UNFORMATTED.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["format", "--write", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_CONTEXT_MODULE_FILE_FORMATTED); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "format_stdin_write_successfully", @@ -338,32 +279,21 @@ fn format_stdin_write_successfully() { #[test] fn lint_stdin_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_LINT_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["lint", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_LINT_BEFORE); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "lint_stdin_successfully", @@ -375,32 +305,21 @@ fn lint_stdin_successfully() { #[test] fn lint_stdin_write_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_LINT_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["lint", "--write", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_LINT_APPLY_AFTER); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "lint_stdin_write_successfully", @@ -412,15 +331,15 @@ fn lint_stdin_write_successfully() { #[test] fn lint_stdin_write_unsafe_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_LINT_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from( [ @@ -436,17 +355,6 @@ fn lint_stdin_write_unsafe_successfully() { assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_LINT_APPLY_UNSAFE_AFTER); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "lint_stdin_write_unsafe_successfully", @@ -458,32 +366,21 @@ fn lint_stdin_write_unsafe_successfully() { #[test] fn check_stdin_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_CHECK_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["check", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_CHECK_BEFORE); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "check_stdin_successfully", @@ -495,32 +392,21 @@ fn check_stdin_successfully() { #[test] fn check_stdin_write_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_CHECK_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from(["check", "--write", "--stdin-file-path", "file.svelte"].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_CHECK_APPLY_AFTER); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "check_stdin_write_successfully", @@ -532,15 +418,15 @@ fn check_stdin_write_successfully() { #[test] fn check_stdin_write_unsafe_successfully() { - let mut fs = MemoryFileSystem::default(); + let fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); console .in_buffer .push(SVELTE_TS_FILE_CHECK_BEFORE.to_string()); - let result = run_cli( - DynRef::Borrowed(&mut fs), + let (fs, result) = run_cli( + fs, &mut console, Args::from( [ @@ -556,17 +442,6 @@ fn check_stdin_write_unsafe_successfully() { assert!(result.is_ok(), "run_cli returned {result:?}"); - let message = console - .out_buffer - .first() - .expect("Console should have written a message"); - - let content = markup_to_string(markup! { - {message.content} - }); - - assert_eq!(content, SVELTE_TS_FILE_CHECK_APPLY_UNSAFE_AFTER); - assert_cli_snapshot(SnapshotPayload::new( module_path!(), "check_stdin_write_unsafe_successfully", diff --git a/crates/biome_cli/tests/cases/handle_vue_files.rs b/crates/biome_cli/tests/cases/handle_vue_files.rs index da261f14b810..9a16660834b1 100644 --- a/crates/biome_cli/tests/cases/handle_vue_files.rs +++ b/crates/biome_cli/tests/cases/handle_vue_files.rs @@ -1,12 +1,9 @@ use crate::run_cli; -use crate::snap_test::{ - assert_cli_snapshot, assert_file_contents, markup_to_string, SnapshotPayload, -}; -use biome_console::{markup, BufferConsole}; +use crate::snap_test::{assert_cli_snapshot, assert_file_contents, SnapshotPayload}; +use biome_console::BufferConsole; use biome_fs::MemoryFileSystem; -use biome_service::DynRef; use bpaf::Args; -use std::path::Path; +use camino::Utf8Path; const VUE_IMPLICIT_JS_FILE_UNFORMATTED: &str = r#" "#; -const VUE_TS_FILE_SAFE_LINTED: &str = r#" -"#; - -const VUE_TS_FILE_UNSAFE_LINTED: &str = r#" -"#; - const VUE_FILE_IMPORTS_BEFORE: &str = r#" "#; -const VUE_TS_FILE_CHECK_APPLY_AFTER: &str = r#" -"#; - -const VUE_TS_FILE_CHECK_APPLY_UNSAFE_AFTER: &str = r#" -"#; - const VUE_TS_FILE_SETUP_GLOBALS: &str = r#"
``` diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/check_stdin_write_unsafe_successfully.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/check_stdin_write_unsafe_successfully.snap index 0a98c5fcd685..7cf98f1404e3 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/check_stdin_write_unsafe_successfully.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/check_stdin_write_unsafe_successfully.snap @@ -1,6 +1,7 @@ --- source: crates/biome_cli/tests/snap_test.rs expression: content +snapshot_kind: text --- # Input messages @@ -22,7 +23,7 @@ var foo: string = ""; import Button from "./components/Button.svelte"; import { Form } from "./components/Form.svelte"; statement(); -const foo = ""; +var foo: string = "";
``` diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_successfully.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_successfully.snap index 2d0ed2d990fc..84c51eb6a9c1 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_successfully.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_successfully.snap @@ -1,6 +1,7 @@ --- source: crates/biome_cli/tests/snap_test.rs expression: content +snapshot_kind: text --- # Input messages @@ -15,7 +16,7 @@ var foo: string = ""; ```block
``` diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_unsafe_successfully.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_unsafe_successfully.snap index 0dc49a8449e6..84c51eb6a9c1 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_unsafe_successfully.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/lint_stdin_write_unsafe_successfully.snap @@ -1,6 +1,7 @@ --- source: crates/biome_cli/tests/snap_test.rs expression: content +snapshot_kind: text --- # Input messages @@ -15,7 +16,7 @@ var foo: string = ""; ```block
``` diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/sorts_imports_check.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/sorts_imports_check.snap index 271aac845eee..7c3d565332ff 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/sorts_imports_check.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/sorts_imports_check.snap @@ -1,6 +1,7 @@ --- source: crates/biome_cli/tests/snap_test.rs expression: content +snapshot_kind: text --- ## `file.svelte` @@ -26,9 +27,9 @@ check ━━━━━━━━━━━━━━━━━━━━━━━━ # Emitted Messages ```block -file.svelte organizeImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +file.svelte assist ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × Import statements could be sorted: + × Not all actions were applied: 1 1 │ - -``` - -# Emitted Messages - -```block -Formatted 1 file(s) in

You are using an outdated browser. Please upgrade your browser to improve your experience and security.

+ 35: >window.jQuery || document.write(' + 41: window.ga = function () { ga.q.push(arguments) }; ga.q = []; ga.l = +new Date; +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html new file mode 100644 index 000000000000..ee191888941c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html @@ -0,0 +1 @@ +a-b- diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html.prettier-snap new file mode 100644 index 000000000000..ee191888941c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-2.html.prettier-snap @@ -0,0 +1 @@ +a-b- diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html new file mode 100644 index 000000000000..bba92f6ce173 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html @@ -0,0 +1 @@ +a trackpad, or a gyroscope. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html.prettier-snap new file mode 100644 index 000000000000..bba92f6ce173 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368-3.html.prettier-snap @@ -0,0 +1 @@ +a trackpad, or a gyroscope. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html new file mode 100644 index 000000000000..05ca98c4967e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html @@ -0,0 +1 @@ +a->b-> diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html.prettier-snap new file mode 100644 index 000000000000..05ca98c4967e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/issue-9368.html.prettier-snap @@ -0,0 +1 @@ +a->b-> diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html new file mode 100644 index 000000000000..8b7f4cff96c1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html @@ -0,0 +1,7 @@ + + + + Anchor + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html.prettier-snap new file mode 100644 index 000000000000..08778fcd1ef1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/more-html.html.prettier-snap @@ -0,0 +1,7 @@ + + + + Anchor + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html new file mode 100644 index 000000000000..42c370a47a16 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html @@ -0,0 +1,15 @@ +text after + + +1 + +1 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.prettier-snap new file mode 100644 index 000000000000..2396b8ca83ad --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.prettier-snap @@ -0,0 +1,12 @@ +text after + + + + + 1 + +1 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap new file mode 100644 index 000000000000..44d93b300530 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements-2.html.snap @@ -0,0 +1,77 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/basics/void-elements-2.html +--- +# Input + +```html +text after + + +1 + +1 + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,12 +1,17 @@ + text after ++ ++ ++text after + + + + +- 1 ++ ++ ++1 + +-1 ++ ++ 1 +``` + +# Output + +```html + +text after + + + + + + +1 + + + 1 +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html new file mode 100644 index 000000000000..72931cad10dd --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html.prettier-snap new file mode 100644 index 000000000000..4eee8d0246ba --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/void-elements.html.prettier-snap @@ -0,0 +1,7 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html new file mode 100644 index 000000000000..29ca0989dc95 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html @@ -0,0 +1,127 @@ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+
+
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + + +

text text text text text text text text text text text text text text

+ +
+ + + + + + +const func = function() { console.log('Hello, there');} +.a{color:#f00} + + + +const func = function() { console.log('Hello, there');} +.a{color:#f00} diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.prettier-snap new file mode 100644 index 000000000000..18e9fcbb014c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.prettier-snap @@ -0,0 +1,258 @@ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
+ pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline inline inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block BLOCK + block block block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline + inline inline
+
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
+ pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline inline inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block BLOCK + block block block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline + inline inline
+
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
+ pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline inline inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block BLOCK + block block block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline + inline inline
+
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
+ pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline inline inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block BLOCK + block block block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline + inline inline
+
+ + +
+
+
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
+ pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline inline inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block BLOCK + block block block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline inline + inline inline +
+
+ + + +
+

+ text text text text text text text text text text text text text text +

+
+ +
+ + + + + + + const func = function() { console.log('Hello, there');} + .a{color:#f00} + + + + + + + +const func = function() { console.log('Hello, there');} +.a{color:#f00} diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.snap new file mode 100644 index 000000000000..8adce091a7e6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/basics/with-colon.html.snap @@ -0,0 +1,797 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/basics/with-colon.html +--- +# Input + +```html + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+ +
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + +
+
+
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block
+
block
BLOCK
block
block
block
+
 pre pr
+e
+ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + block BLOCK block block block + pre pr +e + pre-wrap pr +e-wrap + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + inline inline inline inline
+
+ + + +

text text text text text text text text text text text text text text

+ +
+ + + + + + +const func = function() { console.log('Hello, there');} +.a{color:#f00} + + + +const func = function() { console.log('Hello, there');} +.a{color:#f00} + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,161 +1,165 @@ + +
+- ++ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+-
block
+-
BLOCK
+-
block
+-
block
+-
block
+-
+- pre pr
+-e
+- ++
block
++
BLOCK
++
block
++
block
++
block
++
 pre pr
++e
++ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline inline inline +- ++ inline ++ inline ++ inline ++ inline ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + +- block BLOCK +- block block block +- pre pr e +- pre-wrap pr e-wrap +- ++ block ++ BLOCK ++ block ++ block ++ block ++ pre pr e ++ pre-wrap pr e-wrap ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline +- inline inline
++ inline ++ inline ++ inline ++ inline ++ +
+ + +
+- ++ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+-
block
+-
BLOCK
+-
block
+-
block
+-
block
+-
+- pre pr
+-e
+- ++
block
++
BLOCK
++
block
++
block
++
block
++
 pre pr
++e
++ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline inline inline +- ++ inline ++ inline ++ inline ++ inline ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + +- block BLOCK +- block block block +- pre pr e +- pre-wrap pr e-wrap +- ++ block ++ BLOCK ++ block ++ block ++ block ++ pre pr e ++ pre-wrap pr e-wrap ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline +- inline inline
++ inline ++ inline ++ inline ++ inline ++ +
+ + +
+- ++ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+-
block
+-
BLOCK
+-
block
+-
block
+-
block
+-
+- pre pr
+-e
+- ++
block
++
BLOCK
++
block
++
block
++
block
++
 pre pr
++e
++ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline inline inline +- ++ inline ++ inline ++ inline ++ inline ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + +- block BLOCK +- block block block +- pre pr e +- pre-wrap pr e-wrap +- ++ block ++ BLOCK ++ block ++ block ++ block ++ pre pr e ++ pre-wrap pr e-wrap ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline +- inline inline
++ inline ++ inline ++ inline ++ inline ++ +
+ + +
+- ++ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+-
block
+-
BLOCK
+-
block
+-
block
+-
block
+-
+- pre pr
+-e
+- ++
block
++
BLOCK
++
block
++
block
++
block
++
 pre pr
++e
++ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline inline inline +- ++ inline ++ inline ++ inline ++ inline ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + +- block BLOCK +- block block block +- pre pr e +- pre-wrap pr e-wrap +- ++ block ++ BLOCK ++ block ++ block ++ block ++ pre pr e ++ pre-wrap pr e-wrap ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline +- inline inline
++ inline ++ inline ++ inline ++ inline ++ +
+ + +@@ -164,95 +168,72 @@ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+-
block
+-
BLOCK
+-
block
+-
block
+-
block
+-
+- pre pr
+-e
+- ++
block
++
BLOCK
++
block
++
block
++
block
++
 pre pr
++e
++ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline inline inline +- ++ inline ++ inline ++ inline ++ inline ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + +- block BLOCK +- block block block +- pre pr e +- pre-wrap pr e-wrap +- ++ block ++ BLOCK ++ block ++ block ++ block ++ pre pr e ++ pre-wrap pr e-wrap ++ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + +- inline inline +- inline inline ++ inline ++ inline ++ inline ++ inline +
+
+ + +- ++ +
+

+ text text text text text text text text text text text text text text +

+
+ +
+ + +- +- +- +- +- const func = function() { console.log('Hello, there');} +- .a{color:#f00} ++ ++ ++ ++ ++ ++ const func = function() { console.log('Hello, there');} ++ ++ .a{color:#f00} + +- ++ + + +- ++ + + +-const func = function() { console.log('Hello, there');} +-.a{color:#f00} ++ ++ const func = function() { console.log('Hello, there');} ++ ++.a{color:#f00} +``` + +# Output + +```html + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
 pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block + BLOCK + block + block + block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + +
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
 pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block + BLOCK + block + block + block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + +
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
 pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block + BLOCK + block + block + block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + +
+ + +
+ +
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
 pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block + BLOCK + block + block + block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + +
+ + +
+
+
+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block +
+
block
+
BLOCK
+
block
+
block
+
block
+
 pre pr
+e
+ + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog block + + block + BLOCK + block + block + block + pre pr e + pre-wrap pr e-wrap + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog inline + + inline + inline + inline + inline +
+
+ + + +
+

+ text text text text text text text text text text text text text text +

+
+ + + + + + + + + + const func = function() { console.log('Hello, there');} + + .a{color:#f00} + + + + + + + + + const func = function() { console.log('Hello, there');} + +.a{color:#f00} +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html new file mode 100644 index 000000000000..2aef91d7229f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html @@ -0,0 +1,8 @@ +
+text +
+
+
+text +
+
text
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html.prettier-snap new file mode 100644 index 000000000000..f252ec9961d4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/block.html.prettier-snap @@ -0,0 +1,10 @@ +
+ text +
+
+
text
+
text
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html new file mode 100644 index 000000000000..08a1aab7c598 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html @@ -0,0 +1,8 @@ + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.prettier-snap new file mode 100644 index 000000000000..cf1024f7179e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.prettier-snap @@ -0,0 +1,16 @@ + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.snap new file mode 100644 index 000000000000..5635b6a01b5b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/embed.html.snap @@ -0,0 +1,69 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/bracket-same-line/embed.html +--- +# Input + +```html + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,16 +1,12 @@ + ++alert(1) + ++.a{color: #f00} + ++alert(1) + ++.a{color: #f00} +``` + +# Output + +```html + + + + +``` + +# Lines exceeding max width of 80 characters +``` + 2: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + 6: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html new file mode 100644 index 000000000000..7da285d9aa9c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html @@ -0,0 +1,13 @@ + + +text + + +text + +text + +text +text + +texttexttexttexttext diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.prettier-snap new file mode 100644 index 000000000000..7e46efed5f96 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.prettier-snap @@ -0,0 +1,22 @@ + + text + + +text + + text + +text + text + +texttexttexttexttext diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap new file mode 100644 index 000000000000..7e9c2dc4499b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/inline.html.snap @@ -0,0 +1,87 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/bracket-same-line/inline.html +--- +# Input + +```html + + +text + + +text + +text + +text +text + +texttexttexttexttext + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -12,11 +12,14 @@ + > + text + +-texttext ++ + text + +-texttexttexttexttext ++text ++text ++text ++text ++text +``` + +# Output + +```html + + text + + +text + + text + +text + + text + +text +text +text +text +text +``` + +# Lines exceeding max width of 80 characters +``` + 2: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + 7: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + 11: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + 17: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html new file mode 100644 index 000000000000..7485a593d975 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html @@ -0,0 +1,2 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.prettier-snap new file mode 100644 index 000000000000..05b971a13d54 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.prettier-snap @@ -0,0 +1,7 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.snap new file mode 100644 index 000000000000..6bcd36284824 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/bracket-same-line/void-elements.html.snap @@ -0,0 +1,50 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/bracket-same-line/void-elements.html +--- +# Input + +```html + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -2,6 +2,8 @@ + long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" + src="./1.jpg" + /> +- ++ ++ ++ ++ ++ +``` + +# Output + +```html + + + + + + +``` + +# Lines exceeding max width of 80 characters +``` + 2: long_long_attribute="long_long_long_long_long_long_long_long_long_long_long_value" +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html new file mode 100644 index 000000000000..feff88c0ee92 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html @@ -0,0 +1,16 @@ + + + + + My tITlE + + + +

Hello world!
This is HTML5 Boilerplate.

+ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.prettier-snap new file mode 100644 index 000000000000..30e8a3a415eb --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.prettier-snap @@ -0,0 +1,28 @@ + + + + + My tITlE + + + +

+ Hello world!
+ This is HTML5 Boilerplate. +

+ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.snap new file mode 100644 index 000000000000..8a6d1953aa8b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/case/case.html.snap @@ -0,0 +1,116 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/case/case.html +--- +# Input + +```html + + + + + My tITlE + + + +

Hello world!
This is HTML5 Boilerplate.

+ + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,28 +1,24 @@ +- +- +- +- +- My tITlE +- +- ++ ++ ++ ++ ++ My tITlE ++ ++ + +-

+- Hello world!
++

++ Hello world! ++
+ This is HTML5 Boilerplate. +-

+- +- ++ ++ ASYNC ++ DEFER ++ > + +- ++ +``` + +# Output + +```html + + + + + My tITlE + + + +

+ Hello world! +
+ This is HTML5 Boilerplate. +

+ + + + +``` + +# Lines exceeding max width of 80 characters +``` + 15: window.ga = function () { ga.q.push(arguments) }; ga.q = []; ga.l = +new Date; +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html new file mode 100644 index 000000000000..df7ff8e627d7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html @@ -0,0 +1,4 @@ +John Smith]]> + + a +
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.prettier-snap new file mode 100644 index 000000000000..fa6281ca0205 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.prettier-snap @@ -0,0 +1,7 @@ +John Smith]]> + + a +
+
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.snap new file mode 100644 index 000000000000..163b9fdd0b25 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/cdata/example.html.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/cdata/example.html +--- +# Input + +```html +John Smith]]> + + a +
+ +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,7 +1,8 @@ + John Smith]]> + +- a ++a +
++ > ++
+
+``` + +# Output + +```html +John Smith]]> + +a + +
+
+``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html new file mode 100644 index 000000000000..3bbfb723caf4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html @@ -0,0 +1,3 @@ + + +123 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.prettier-snap new file mode 100644 index 000000000000..3bbfb723caf4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.prettier-snap @@ -0,0 +1,3 @@ + + +123 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap new file mode 100644 index 000000000000..5147537c5209 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/before-text.html.snap @@ -0,0 +1,31 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/before-text.html +--- +# Input + +```html + + +123 + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,3 +1,2 @@ + +- + 123 +``` + +# Output + +```html + +123 +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html new file mode 100644 index 000000000000..d9b23a7310c1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html @@ -0,0 +1,2 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.prettier-snap new file mode 100644 index 000000000000..d9b23a7310c1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/bogus.html.prettier-snap @@ -0,0 +1,2 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html new file mode 100644 index 000000000000..315cff130a29 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.prettier-snap new file mode 100644 index 000000000000..8d18b3867762 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.prettier-snap @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap new file mode 100644 index 000000000000..804e3197bbc8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/conditional.html.snap @@ -0,0 +1,416 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/conditional.html +--- +# Input + +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,29 +1,29 @@ +- ++ + + +- +- +- +- +- ++ ++ ++ ++ ++ + + + +- ++ + + + + + + +- ++ + + + + + + +- ++ + + + +@@ -31,40 +31,38 @@ + + + +-
++
+ +-
++
+ +-
++
+ + +- ++ + + + + + + +- ++ + + + + + + +- ++ + +- +- +- +- ++ + + + + +- ++ + +- ++ + + + +``` + +# Output + +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +# Errors +``` +conditional.html:14:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 12 │ + 13 │ + > 14 │ + │ ^ + 15 │ + 16 │ + + i Expected an element name here. + + 12 │ + 13 │ + > 14 │ + │ ^ + 15 │ + 16 │ + +conditional.html:21:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 19 │ + 20 │ + > 21 │ + │ ^ + 22 │ + 23 │ + + i Expected an element name here. + + 19 │ + 20 │ + > 21 │ + │ ^ + 22 │ + 23 │ + +conditional.html:28:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 26 │ + 27 │ + > 28 │ + │ ^ + 29 │ + 30 │ + + i Expected an element name here. + + 26 │ + 27 │ + > 28 │ + │ ^ + 29 │ + 30 │ + +conditional.html:43:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 41 │ + 42 │ + > 43 │ + │ ^ + 44 │ + 45 │ + + i Expected an element name here. + + 41 │ + 42 │ + > 43 │ + │ ^ + 44 │ + 45 │ + +conditional.html:50:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 48 │ + 49 │ + > 50 │ + │ ^ + 51 │ + 52 │ + + i Expected an element name here. + + 48 │ + 49 │ + > 50 │ + │ ^ + 51 │ + 52 │ + +conditional.html:57:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 55 │ + 56 │ + > 57 │ + │ ^ + 58 │ + 59 │ + + i Expected an element name here. + + 55 │ + 56 │ + > 57 │ + │ ^ + 58 │ + 59 │ + +conditional.html:64:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 62 │ + 63 │ + > 64 │ + │ ^ + 65 │ + 66 │ + 66 │ +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html new file mode 100644 index 000000000000..2e226da2168c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.prettier-snap new file mode 100644 index 000000000000..f957f100603e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.prettier-snap @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap new file mode 100644 index 000000000000..a281bf3500a5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/for_debugging.html.snap @@ -0,0 +1,62 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/for_debugging.html +--- +# Input + +```html + + + + + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html new file mode 100644 index 000000000000..079bb7dd73d6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html @@ -0,0 +1,13 @@ + + + + + + + + +

This is a paragraph.

+ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.prettier-snap new file mode 100644 index 000000000000..928e6077308e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.prettier-snap @@ -0,0 +1,11 @@ + + + + + + + +

This is a paragraph.

+ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.snap new file mode 100644 index 000000000000..7e7c242e14f9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/hidden.html.snap @@ -0,0 +1,52 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/hidden.html +--- +# Input + +```html + + + + + + + + +

This is a paragraph.

+ + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + +``` + +# Output + +```html + + + + + + + +

This is a paragraph.

+ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html new file mode 100644 index 000000000000..937c05d9e181 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html @@ -0,0 +1,56 @@ +
  • First
  • Second
  • Second
+ab + +ab + +ab + +123456 + +123456 + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.prettier-snap new file mode 100644 index 000000000000..c1fc040526a9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.prettier-snap @@ -0,0 +1,71 @@ +
    + +
  • First
  • + +
  • Second
  • + +
  • Second
  • +
+ab + +ab + +ab + +123456 123456 + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.snap new file mode 100644 index 000000000000..92191f479e7c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/comments/surrounding-empty-line.html.snap @@ -0,0 +1,214 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/comments/surrounding-empty-line.html +--- +# Input + +```html +
  • First
  • Second
  • Second
+ab + +ab + +ab + +123456 + +123456 + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -25,32 +25,40 @@ + 789 + + +---> ++--> ++ + ab ++--> ++ a ++ ++ b ++ + + ab ++--> ++ a ++ ++ b ++ + + ab ++ a ++ ++ b ++ +- + 123456 123456 + + +
  • First
  • + +
  • Second
  • + +
  • Second
  • + + + + a + + b + + + + a + + b + + + + a + + b + +123456 123456 + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html new file mode 100644 index 000000000000..6f29e9e9f954 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html.prettier-snap new file mode 100644 index 000000000000..6f29e9e9f954 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/empty.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html new file mode 100644 index 000000000000..3f2dcd1ca3ff --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html @@ -0,0 +1,17 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.prettier-snap new file mode 100644 index 000000000000..800df5abf0e3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.prettier-snap @@ -0,0 +1,17 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.snap new file mode 100644 index 000000000000..33efd24fd57e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/less.html.snap @@ -0,0 +1,65 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/css/less.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -8,7 +8,7 @@ + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html new file mode 100644 index 000000000000..7c7a677c7bb3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html @@ -0,0 +1,7 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.prettier-snap new file mode 100644 index 000000000000..945a2b6a6924 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.prettier-snap @@ -0,0 +1,13 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.snap new file mode 100644 index 000000000000..7ee539ef4633 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/postcss.html.snap @@ -0,0 +1,52 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/css/postcss.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,13 +1,7 @@ + + + +``` + +# Output + +```html + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html new file mode 100644 index 000000000000..0da28a950c9f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html @@ -0,0 +1,32 @@ + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.prettier-snap new file mode 100644 index 000000000000..bd0868c3962e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.prettier-snap @@ -0,0 +1,32 @@ + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.snap new file mode 100644 index 000000000000..ef07ce54b010 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/scss.html.snap @@ -0,0 +1,120 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/css/scss.html +--- +# Input + +```html + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1,5 @@ + + + + + +``` + +# Output + +```html + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html new file mode 100644 index 000000000000..2e1e0b24233d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html @@ -0,0 +1,14 @@ + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.prettier-snap new file mode 100644 index 000000000000..6bf6ebe93fca --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.prettier-snap @@ -0,0 +1,21 @@ + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.snap new file mode 100644 index 000000000000..50f170416e00 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/simple.html.snap @@ -0,0 +1,71 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/css/simple.html +--- +# Input + +```html + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,17 +1,10 @@ +- ++ + + + Sample styled page +- ++ + + + +``` + +# Output + +```html + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html new file mode 100644 index 000000000000..84dccb63aff2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html @@ -0,0 +1,8 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.prettier-snap new file mode 100644 index 000000000000..9fa2f7a0298e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.prettier-snap @@ -0,0 +1,12 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.snap new file mode 100644 index 000000000000..8791afbdfa14 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/css/single-style.html.snap @@ -0,0 +1,48 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/css/single-style.html +--- +# Input + +```html + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,8 +1,4 @@ +- ++ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html new file mode 100644 index 000000000000..0a48c9b72f52 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html @@ -0,0 +1,11 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.prettier-snap new file mode 100644 index 000000000000..d0d672a5df97 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.prettier-snap @@ -0,0 +1,10 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.snap new file mode 100644 index 000000000000..9248790f7542 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_frameset.html.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/doctype_declarations/html4.01_frameset.html +--- +# Input + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + An HTML standard template +``` + +# Output + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + +``` + +# Lines exceeding max width of 80 characters +``` + 1: +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html new file mode 100644 index 000000000000..1171d316eab0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html @@ -0,0 +1,11 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.prettier-snap new file mode 100644 index 000000000000..414e9bcbe606 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.prettier-snap @@ -0,0 +1,10 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.snap new file mode 100644 index 000000000000..8a7e184d51ab --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_strict.html.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/doctype_declarations/html4.01_strict.html +--- +# Input + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + An HTML standard template +``` + +# Output + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + +``` + +# Lines exceeding max width of 80 characters +``` + 1: +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html new file mode 100644 index 000000000000..101498871a23 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html @@ -0,0 +1,11 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.prettier-snap new file mode 100644 index 000000000000..83e6d53d0a3b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.prettier-snap @@ -0,0 +1,10 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.snap new file mode 100644 index 000000000000..6c0ad9beb9ac --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html4.01_transitional.html.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/doctype_declarations/html4.01_transitional.html +--- +# Input + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + An HTML standard template +``` + +# Output + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + +``` + +# Lines exceeding max width of 80 characters +``` + 1: +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html new file mode 100644 index 000000000000..218980b98be5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html @@ -0,0 +1,10 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.prettier-snap new file mode 100644 index 000000000000..7579cd26aa87 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.prettier-snap @@ -0,0 +1,10 @@ + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.snap new file mode 100644 index 000000000000..869a9bcde249 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/html5.html.snap @@ -0,0 +1,48 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/doctype_declarations/html5.html +--- +# Input + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + An HTML standard template +``` + +# Output + +```html + + + + An HTML standard template + + + +

    … Your HTML content here …

    + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html new file mode 100644 index 000000000000..f0e7c7c16b0a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html.prettier-snap new file mode 100644 index 000000000000..f0e7c7c16b0a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/ibm_system.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html new file mode 100644 index 000000000000..77a55353f20c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html.prettier-snap new file mode 100644 index 000000000000..77a55353f20c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/legacy_string.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html new file mode 100644 index 000000000000..9c93f5f54e2d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html @@ -0,0 +1,3 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html.prettier-snap new file mode 100644 index 000000000000..19f0af87d7d0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_frameset.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html new file mode 100644 index 000000000000..2c86ce1c5d41 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html @@ -0,0 +1,2 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html.prettier-snap new file mode 100644 index 000000000000..7b370d1deab3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_strict.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html new file mode 100644 index 000000000000..51349972465f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html @@ -0,0 +1,3 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html.prettier-snap new file mode 100644 index 000000000000..10fb0ad1f5a8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.0_transitional.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html new file mode 100644 index 000000000000..5ec2cfdca7a6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html @@ -0,0 +1,22 @@ + + + + + XHTML markup + + +
    +

    Sample XHTML page

    +
    +
    + Beep +
    +

    Bar Foo,
    + Foo,
    + Bar
    + Foo

    +

    String

    +
    +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.prettier-snap new file mode 100644 index 000000000000..09f37d8b4e10 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.prettier-snap @@ -0,0 +1,30 @@ + + + + + XHTML markup + + +
    +

    Sample XHTML page

    +
    +
    + Beep +
    +

    + Bar Foo,
    + Foo,
    + Bar
    + Foo +

    +

    String

    +
    +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.snap new file mode 100644 index 000000000000..aef9b07b4eec --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/doctype_declarations/xhtml1.1.html.snap @@ -0,0 +1,117 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/doctype_declarations/xhtml1.1.html +--- +# Input + +```html + + + + + XHTML markup + + +
    +

    Sample XHTML page

    +
    +
    + Beep +
    +

    Bar Foo,
    + Foo,
    + Bar
    + Foo

    +

    String

    +
    +
    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -4,7 +4,7 @@ + + XHTML markup + +- ++ +
    +

    Sample XHTML page

    +
    +@@ -17,13 +17,18 @@ + vspace="20" + /> +
    +-

    +- Bar Foo,
    +- Foo,
    +- Bar
    ++

    ++ Bar Foo, ++
    ++ Foo, ++
    ++ Bar ++
    + Foo +

    +-

    String

    ++

    ++ String ++

    +
    +
    + +``` + +# Output + +```html + + + + + XHTML markup + + +
    +

    Sample XHTML page

    +
    +
    + Beep +
    +

    + Bar Foo, +
    + Foo, +
    + Bar +
    + Foo +

    +

    + String +

    +
    +
    + + +``` + +# Lines exceeding max width of 80 characters +``` + 1: +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html new file mode 100644 index 000000000000..165bcc9e3ebe --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html @@ -0,0 +1,9 @@ +---mycustomparser + +title: Hello +slug: home + +--- + +

    + Hello world!

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.prettier-snap new file mode 100644 index 000000000000..493adf20a1bd --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.prettier-snap @@ -0,0 +1,8 @@ +---mycustomparser + +title: Hello +slug: home + +--- + +

    Hello world!

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.snap new file mode 100644 index 000000000000..b480e1b40520 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/custom-parser.html.snap @@ -0,0 +1,44 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/front-matter/custom-parser.html +--- +# Input + +```html +---mycustomparser + +title: Hello +slug: home + +--- + +

    + Hello world!

    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,8 +1,3 @@ +----mycustomparser +- +-title: Hello +-slug: home +- +---- ++---mycustomparser title: Hello slug: home --- + +

    Hello world!

    +``` + +# Output + +```html +---mycustomparser title: Hello slug: home --- + +

    Hello world!

    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html new file mode 100644 index 000000000000..eca1bfac3206 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html @@ -0,0 +1,5 @@ +--- +--- + +

    + Hello world!

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.prettier-snap new file mode 100644 index 000000000000..dca897bcbb99 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.prettier-snap @@ -0,0 +1,4 @@ +--- +--- + +

    Hello world!

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.snap new file mode 100644 index 000000000000..dfb9f0299e4e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty.html.snap @@ -0,0 +1,36 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/front-matter/empty.html +--- +# Input + +```html +--- +--- + +

    + Hello world!

    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,3 @@ +---- +---- ++--- --- + +

    Hello world!

    +``` + +# Output + +```html +--- --- + +

    Hello world!

    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html new file mode 100644 index 000000000000..41c2577f56cf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html @@ -0,0 +1,6 @@ +--- +--- + +
    +--- +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.prettier-snap new file mode 100644 index 000000000000..72539469c61e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.prettier-snap @@ -0,0 +1,4 @@ +--- +--- + +
    ---
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap new file mode 100644 index 000000000000..c721b265afaa --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/front-matter/empty2.html +--- +# Input + +```html +--- +--- + +
    +--- +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,3 @@ +---- +---- ++--- --- + +
    ---
    +``` + +# Output + +```html +--- --- + +
    ---
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html new file mode 100644 index 000000000000..b7f318b23b52 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html @@ -0,0 +1,5 @@ +--- +layout: foo +--- +Test abc. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.prettier-snap new file mode 100644 index 000000000000..e37dadb431dc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.prettier-snap @@ -0,0 +1,5 @@ +--- +layout: foo +--- + +Test abc. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.snap new file mode 100644 index 000000000000..32af9a63a533 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042-no-empty-line.html.snap @@ -0,0 +1,35 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/front-matter/issue-9042-no-empty-line.html +--- +# Input + +```html +--- +layout: foo +--- +Test abc. + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1 @@ +---- +-layout: foo +---- +- +-Test abc. ++--- layout: foo --- Test abc. +``` + +# Output + +```html +--- layout: foo --- Test abc. +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html new file mode 100644 index 000000000000..e93627d7c3ba --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html @@ -0,0 +1,6 @@ +--- +layout: foo +--- + +Test abc. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.prettier-snap new file mode 100644 index 000000000000..e37dadb431dc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.prettier-snap @@ -0,0 +1,5 @@ +--- +layout: foo +--- + +Test abc. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.snap new file mode 100644 index 000000000000..448248d2c041 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/front-matter/issue-9042.html.snap @@ -0,0 +1,36 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/front-matter/issue-9042.html +--- +# Input + +```html +--- +layout: foo +--- + +Test abc. + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1 @@ +---- +-layout: foo +---- +- +-Test abc. ++--- layout: foo --- Test abc. +``` + +# Output + +```html +--- layout: foo --- Test abc. +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html new file mode 100644 index 000000000000..28a236a696d6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html @@ -0,0 +1,9 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.prettier-snap new file mode 100644 index 000000000000..221f55b8bc24 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.prettier-snap @@ -0,0 +1,14 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.snap new file mode 100644 index 000000000000..02aa675120f0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/handlebars-venerable/template.html.snap @@ -0,0 +1,64 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/handlebars-venerable/template.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,14 +1,9 @@ + + + +``` + +# Output + +```html + + + +``` + +# Lines exceeding max width of 80 characters +``` + 8: {{component arg1='hey' arg2=(helper this.arg7 this.arg4) arg3=anotherone arg6=this.arg8}} +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html new file mode 100644 index 000000000000..89c1110e918d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html @@ -0,0 +1,26 @@ +
    Fuga magnam facilis. Voluptatem quaerat porro.{{ + + +x => { + const hello = 'world' + return hello; +} + + + +}} Magni consectetur in et molestias neque esse voluptatibus voluptas. {{ + + + some_variable + + + +}} Eum quia nihil nulla esse. Dolorem asperiores vero est error {{ + + preserve + + invalid + + interpolation + +}} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.prettier-snap new file mode 100644 index 000000000000..0558c233341d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.prettier-snap @@ -0,0 +1,8 @@ + +
    + Fuga magnam facilis. Voluptatem quaerat porro.{{ x => { const hello = 'world' + return hello; } }} Magni consectetur in et molestias neque esse voluptatibus + voluptas. {{ some_variable }} Eum quia nihil nulla esse. Dolorem asperiores + vero est error {{ preserve invalid interpolation }} reprehenderit voluptates + minus {{console.log( short_interpolation )}} nemo. +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html new file mode 100644 index 000000000000..5d2a3a05bf9c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html.prettier-snap new file mode 100644 index 000000000000..5d2a3a05bf9c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/empty.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html new file mode 100644 index 000000000000..abbeb6b1010d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html @@ -0,0 +1,23 @@ + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.prettier-snap new file mode 100644 index 000000000000..322d4dc6b8f8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.prettier-snap @@ -0,0 +1,25 @@ + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.snap new file mode 100644 index 000000000000..635a63f3d4e1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/js.html.snap @@ -0,0 +1,86 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/js.html +--- +# Input + +```html + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -14,12 +14,10 @@ + alert(message); + + + ++ import lib from './lib.js'; ++ ++ function myFunction() { return 'foo'; } ++ +``` + +# Output + +```html + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html new file mode 100644 index 000000000000..a2dcd0189c2e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html @@ -0,0 +1,16 @@ + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.prettier-snap new file mode 100644 index 000000000000..1223a7c28d5d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.prettier-snap @@ -0,0 +1,18 @@ + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.snap new file mode 100644 index 000000000000..c907ec07b3dc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/simple.html.snap @@ -0,0 +1,67 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/simple.html +--- +# Input + +```html + + + + Sample styled page + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,10 +1,8 @@ +- ++ + + + Sample styled page +- ++ + + + + +

    Sample styled page

    +

    This page is just a demo.

    + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html new file mode 100644 index 000000000000..bd93db13c618 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html @@ -0,0 +1,4 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.prettier-snap new file mode 100644 index 000000000000..db5f01ea2930 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.prettier-snap @@ -0,0 +1,6 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.snap new file mode 100644 index 000000000000..6ec5a99beaf4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/single-script.html.snap @@ -0,0 +1,38 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/single-script.html +--- +# Input + +```html + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,6 +1,4 @@ +- ++ + +``` + +# Output + +```html + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html new file mode 100644 index 000000000000..0ad293f6fa1b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html @@ -0,0 +1,4 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.prettier-snap new file mode 100644 index 000000000000..c61f0fd35a85 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.prettier-snap @@ -0,0 +1,4 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.snap new file mode 100644 index 000000000000..1508d8d81716 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/something-else.html.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/something-else.html +--- +# Input + +```html + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ + +``` + +# Output + +```html + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html new file mode 100644 index 000000000000..25a82c31edbf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.prettier-snap new file mode 100644 index 000000000000..229615770474 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.prettier-snap @@ -0,0 +1,15 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.snap new file mode 100644 index 000000000000..0e9c3a8b9401 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/template-literal.html.snap @@ -0,0 +1,75 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/template-literal.html +--- +# Input + +```html + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,15 +1,15 @@ +- ++ + +- ++ + + ++ } ++ + + +``` + +# Output + +```html + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html new file mode 100644 index 000000000000..777a1b70f603 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html @@ -0,0 +1,78 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.prettier-snap new file mode 100644 index 000000000000..1845ccbbd890 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.prettier-snap @@ -0,0 +1,89 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.snap new file mode 100644 index 000000000000..ae1d2b4a6091 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/js/typescript.html.snap @@ -0,0 +1,293 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/js/typescript.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,13 +1,9 @@ + + +``` + +# Output + +```html + + + +``` + +# Lines exceeding max width of 80 characters +``` + 4: constructor(public firstName: string, public middleInitial: string, public lastName: string) { + 25: constructor(public firstName: string, public middleInitial: string, public lastName: string) { + 44: class CommentBox extends React.Component<{ url: string, pollInterval: number}, CommentData> { +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html new file mode 100644 index 000000000000..a8543652168d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html @@ -0,0 +1,4 @@ +
    + +

    Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long

    +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.prettier-snap new file mode 100644 index 000000000000..d713cfaca20e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.prettier-snap @@ -0,0 +1,7 @@ +
    + +

    Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long + Long Long Long

    +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.snap new file mode 100644 index 000000000000..001b8854ebfc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/magic_comments/display.html.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/magic_comments/display.html +--- +# Input + +```html +
    + +

    Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long

    +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,7 +1,7 @@ +
    + +-

    Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long +- Long Long Long

    ++

    ++ Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long ++ Long Long Long ++

    +
    +``` + +# Output + +```html +
    + +

    + Long Long Long Long Long Long Long Long Long Long Long Long Long Long Long + Long Long Long +

    +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html new file mode 100644 index 000000000000..35391ca1ba7f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.prettier-snap new file mode 100644 index 000000000000..e072a1ad510b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.prettier-snap @@ -0,0 +1,11 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.snap new file mode 100644 index 000000000000..aed20c74c4e9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/css/html-with-css-style.html.snap @@ -0,0 +1,60 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/css/html-with-css-style.html +--- +# Input + +```html + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,10 +1,10 @@ +- ++ + + + + + +``` + +# Output + +```html + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html new file mode 100644 index 000000000000..3e7634b988dc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.prettier-snap new file mode 100644 index 000000000000..11ec69ad9b75 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.prettier-snap @@ -0,0 +1,9 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.snap new file mode 100644 index 000000000000..c2c38ebcc58a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/html-with-js-script.html.snap @@ -0,0 +1,55 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/js/html-with-js-script.html +--- +# Input + +```html + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,8 +1,9 @@ +- ++ + + + + + +``` + +# Output + +```html + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html new file mode 100644 index 000000000000..575e07b2efd7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html @@ -0,0 +1,13 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.prettier-snap new file mode 100644 index 000000000000..5fcdf92af6bc --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.prettier-snap @@ -0,0 +1,17 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.snap new file mode 100644 index 000000000000..c1d114c483b1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/js/script-tag-escaping.html.snap @@ -0,0 +1,61 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/js/script-tag-escaping.html +--- +# Input + +```html + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -4,11 +4,7 @@ + document.write(/* HTML */ \` + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html new file mode 100644 index 000000000000..d1305e10f6d2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.prettier-snap new file mode 100644 index 000000000000..e2ff3342d26f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.prettier-snap @@ -0,0 +1,12 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.snap new file mode 100644 index 000000000000..dc930e0d05a1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/markdown/html-with-markdown-script.html.snap @@ -0,0 +1,61 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/markdown/html-with-markdown-script.html +--- +# Input + +```html + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,11 +1,10 @@ +- ++ + + + + + +``` + +# Output + +```html + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html new file mode 100644 index 000000000000..1a67dbfc8415 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.prettier-snap new file mode 100644 index 000000000000..edd984e3f052 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.prettier-snap @@ -0,0 +1,15 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.snap new file mode 100644 index 000000000000..ac318f8ecbc7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/ts/html-with-ts-script.html.snap @@ -0,0 +1,87 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/ts/html-with-ts-script.html +--- +# Input + +```html + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,14 +1,18 @@ +- ++ + + + + + +``` + +# Output + +```html + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html new file mode 100644 index 000000000000..8d1f258f0720 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html @@ -0,0 +1,34 @@ + + + + + + Document + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.prettier-snap new file mode 100644 index 000000000000..4ef7cbbad3e2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.prettier-snap @@ -0,0 +1,34 @@ + + + + + + Document + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.snap new file mode 100644 index 000000000000..9c4ef9144be2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/multiparser/unknown/unknown-lang.html.snap @@ -0,0 +1,142 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/multiparser/unknown/unknown-lang.html +--- +# Input + +```html + + + + + + Document + + + + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + +@@ -7,28 +7,28 @@ + + + ++.prettier { ++content: ++"awesome" ++ } ++ + + ++prettier.is ++ .awesome( ++) ++ + + ++prettier.is ++ .awesome( ++) ++ + + ++prettier.is ++ .awesome( ++) ++ + + +``` + +# Output + +```html + + + + + + Document + + + + + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html new file mode 100644 index 000000000000..3174faf3fba6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html @@ -0,0 +1,20 @@ +
    + +
    + +
    + + + +
    + 123123123123 + + 123123 +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.prettier-snap new file mode 100644 index 000000000000..52129e0ceaac --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.prettier-snap @@ -0,0 +1,16 @@ +
    + +
    + +
    + + + +
    + 123123123123 + + 123123 +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.snap new file mode 100644 index 000000000000..1954324f59b9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/next_empty_line/standalone-end-marker.html.snap @@ -0,0 +1,69 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/next_empty_line/standalone-end-marker.html +--- +# Input + +```html +
    + +
    + +
    + + + +
    + 123123123123 + + 123123 +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -11,6 +11,5 @@ + href="#123123123123123131231312321312312312312312312312312313123123123123123" + >123123123123 +- + 123123 +
    +``` + +# Output + +```html +
    + +
    + +
    + + + +
    + 123123123123 + 123123 +
    +``` + +# Lines exceeding max width of 80 characters +``` + 11: href="#123123123123123131231312321312312312312312312312312313123123123123123" +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html new file mode 100644 index 000000000000..a6cde3bcc855 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.prettier-snap new file mode 100644 index 000000000000..12ff25941f30 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.prettier-snap @@ -0,0 +1,3 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap new file mode 100644 index 000000000000..ec54648387fd --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/no-pragma.html.snap @@ -0,0 +1,77 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/pragma/no-pragma.html +--- +# Input + +```html + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,3 +1,4 @@ + + +- ++ ++ +``` + +# Output + +```html + + + + +``` + +# Errors +``` +no-pragma.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + + i Expected an element name here. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + +no-pragma.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a closing tag but instead found the end of the file. + + 5 │ + 6 │ + > 7 │ + │ + + i Expected a closing tag here. + + 5 │ + 6 │ + > 7 │ + │ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html new file mode 100644 index 000000000000..3c4a58159d2b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.prettier-snap new file mode 100644 index 000000000000..01078b2fbd92 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.prettier-snap @@ -0,0 +1,3 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap new file mode 100644 index 000000000000..df6335f4be27 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma-2.html.snap @@ -0,0 +1,77 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/pragma/with-pragma-2.html +--- +# Input + +```html + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,3 +1,4 @@ + + +- ++ ++ +``` + +# Output + +```html + + + + +``` + +# Errors +``` +with-pragma-2.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + + i Expected an element name here. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + +with-pragma-2.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a closing tag but instead found the end of the file. + + 5 │ + 6 │ + > 7 │ + │ + + i Expected a closing tag here. + + 5 │ + 6 │ + > 7 │ + │ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html new file mode 100644 index 000000000000..9c2e13e2e611 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.prettier-snap new file mode 100644 index 000000000000..c527078283c2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.prettier-snap @@ -0,0 +1,3 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap new file mode 100644 index 000000000000..e4cf04ca2cbd --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/pragma/with-pragma.html.snap @@ -0,0 +1,77 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/pragma/with-pragma.html +--- +# Input + +```html + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,3 +1,4 @@ + + +- ++ ++ +``` + +# Output + +```html + + + + +``` + +# Errors +``` +with-pragma.html:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found '!'. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + + i Expected an element name here. + + 3 │ + > 4 │ + │ ^ + 5 │ + 6 │ + +with-pragma.html:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a closing tag but instead found the end of the file. + + 5 │ + 6 │ + > 7 │ + │ + + i Expected a closing tag here. + + 5 │ + 6 │ + > 7 │ + │ + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html new file mode 100644 index 000000000000..3072927ed3ab --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html @@ -0,0 +1,3 @@ +123456 + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.prettier-snap new file mode 100644 index 000000000000..5fa1e48a7762 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.prettier-snap @@ -0,0 +1,4 @@ +123456 + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap new file mode 100644 index 000000000000..9f862c301eb0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/cases.html.snap @@ -0,0 +1,38 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/prettier_ignore/cases.html +--- +# Input + +```html +123456 + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,5 @@ + 123456 + +- ++ ++ ++ +``` + +# Output + +```html +123456 + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html new file mode 100644 index 000000000000..62dfd302c068 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html @@ -0,0 +1,43 @@ + + + + + + Title + + + +

    + Test Test Test +

    + + +

    + Test Test Test +

    + + +
    • First
    • Second1
      String
    + + +
    + + + + + +
    + + +
    • First
    • Second
    + + +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.prettier-snap new file mode 100644 index 000000000000..f90689a28bc6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.prettier-snap @@ -0,0 +1,43 @@ + + + + + + Title + + + +

    + Test Test Test +

    + + +

    + Test Test Test +

    + + +
    • First
    • Second1
      String
    + + +
    + + + + + +
    + + +
    • First
    • Second
    + + +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap new file mode 100644 index 000000000000..c3643cb13f2f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/document.html.snap @@ -0,0 +1,167 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/prettier_ignore/document.html +--- +# Input + +```html + + + + + + Title + + + +

    + Test Test Test +

    + + +

    + Test Test Test +

    + + +
    • First
    • Second1
      String
    + + +
    + + + + + +
    + + +
    • First
    • Second
    + + +
    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -7,35 +7,36 @@ + + + +-

    +- Test Test Test +-

    ++

    Test Test Test

    + + +-

    +- Test Test Test +-

    ++

    Test Test Test

    + + +-
    • First
    • Second1
      String
    ++
      ++
    • First
    • ++
    • ++ Second1 ++
      ++
      String
      ++
      ++
    • ++
    + + +-
    +- +- +- +- +- +-
    ++
    + + +-
    • First
    • Second
    ++
      ++ ++
    • First
    • ++ ++
    • Second
    • ++ ++
    + + +
    +``` + +# Output + +```html + + + + + + Title + + + +

    Test Test Test

    + + +

    Test Test Test

    + + +
      +
    • First
    • +
    • + Second1 +
      +
      String
      +
      +
    • +
    + + +
    + + +
      + +
    • First
    • + +
    • Second
    • + +
    + + +
    + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html new file mode 100644 index 000000000000..bffb3a754b05 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html @@ -0,0 +1,16 @@ + +A super long string that has been marked as ignore because it was probably generated by some script. + +

    + Just some ordinary text that should be wrapped up because it is super long and has not been marked as ignore. +

    + +

    + + A super long string that has been marked as ignore because it was probably generated by some script. +

    + + +| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | +| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | +| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | \ No newline at end of file diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.prettier-snap new file mode 100644 index 000000000000..40ad9f1799af --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.prettier-snap @@ -0,0 +1,17 @@ + +A super long string that has been marked as ignore because it was probably generated by some script. + +

    + Just some ordinary text that should be wrapped up because it is super long and + has not been marked as ignore. +

    + +

    + + A super long string that has been marked as ignore because it was probably generated by some script. +

    + + +| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | +| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | +| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap new file mode 100644 index 000000000000..2f96e1c210c4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/prettier_ignore/long_lines.html.snap @@ -0,0 +1,80 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/prettier_ignore/long_lines.html +--- +# Input + +```html + +A super long string that has been marked as ignore because it was probably generated by some script. + +

    + Just some ordinary text that should be wrapped up because it is super long and has not been marked as ignore. +

    + +

    + + A super long string that has been marked as ignore because it was probably generated by some script. +

    + + +| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | +| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | +| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1,5 @@ +- +-A super long string that has been marked as ignore because it was probably generated by some script. ++A super long string that has been ++marked as ignore because it was probably generated by some script. + +

    + Just some ordinary text that should be wrapped up because it is super long and +@@ -7,11 +7,11 @@ +

    + +

    +- +- A super long string that has been marked as ignore because it was probably generated by some script. ++ A super long string that has been ++ marked as ignore because it was probably generated by some script. +

    + +- +-| Dogs | Cats | Weasels | Bats | Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | +-| ---- | ---- | ------- | ---- | ---- | ---- | --------- | --------- | ---- | ------ | +-| 1 | 1 | 0 | 0 | 1 | 1 | 5 | 16 | 4 | 0 | ++| Dogs | Cats | Weasels | Bats | ++Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | | ---- | ---- | ------- | ++---- | ---- | ---- | --------- | --------- | ---- | ------ | | 1 | 1 | 0 | 0 | 1 ++| 1 | 5 | 16 | 4 | 0 | +``` + +# Output + +```html +A super long string that has been +marked as ignore because it was probably generated by some script. + +

    + Just some ordinary text that should be wrapped up because it is super long and + has not been marked as ignore. +

    + +

    + A super long string that has been + marked as ignore because it was probably generated by some script. +

    + +| Dogs | Cats | Weasels | Bats | +Pigs | Mice | Hedgehogs | Capybaras | Rats | Tigers | | ---- | ---- | ------- | +---- | ---- | ---- | --------- | --------- | ---- | ------ | | 1 | 1 | 0 | 0 | 1 +| 1 | 5 | 16 | 4 | 0 | +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html new file mode 100644 index 000000000000..75ff3bed275a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html @@ -0,0 +1,14 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.prettier-snap new file mode 100644 index 000000000000..751b96724fae --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.prettier-snap @@ -0,0 +1,10 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.snap new file mode 100644 index 000000000000..4555b7b91106 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/babel.html.snap @@ -0,0 +1,70 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/script/babel.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,10 +1,14 @@ + + + +``` + +# Output + +```html + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html new file mode 100644 index 000000000000..bce410cd6b81 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html @@ -0,0 +1,12 @@ + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.prettier-snap new file mode 100644 index 000000000000..b68983f76352 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.prettier-snap @@ -0,0 +1,11 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.snap new file mode 100644 index 000000000000..16b6306103c2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/legacy.html.snap @@ -0,0 +1,63 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/script/legacy.html +--- +# Input + +```html + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,11 +1,11 @@ + + + +``` + +# Output + +```html + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html b/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html new file mode 100644 index 000000000000..331944ba68bf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html.prettier-snap new file mode 100644 index 000000000000..331944ba68bf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/module-attributes.html.prettier-snap @@ -0,0 +1 @@ + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html new file mode 100644 index 000000000000..47c5de713c86 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html @@ -0,0 +1,17 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.prettier-snap new file mode 100644 index 000000000000..4d8aa967405e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.prettier-snap @@ -0,0 +1,17 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.snap new file mode 100644 index 000000000000..235950d6c352 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/module.html.snap @@ -0,0 +1,85 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/script/module.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,17 +1,17 @@ + + + +``` + +# Output + +```html + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html new file mode 100644 index 000000000000..eff326ee54f1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.prettier-snap new file mode 100644 index 000000000000..06a6cda0fda6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.prettier-snap @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.snap new file mode 100644 index 000000000000..180f03a3d2d8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/script/script.html.snap @@ -0,0 +1,158 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/script/script.html +--- +# Input + +```html + + + + + + + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,21 +1,21 @@ + + + + + + + +@@ -28,14 +28,17 @@ + + + + +- ++ + + ++ +``` + +# Output + +```html + + + + + + + + + + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html b/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html new file mode 100644 index 000000000000..0295f0c19904 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html @@ -0,0 +1,21 @@ +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + + + +bar + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html.prettier-snap new file mode 100644 index 000000000000..4583224446c2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/single-attribute-per-line/single-attribute-per-line.html.prettier-snap @@ -0,0 +1,30 @@ +
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
    + + + +bar + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html b/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html new file mode 100644 index 000000000000..3ec7accae1bb --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html.prettier-snap new file mode 100644 index 000000000000..15d08718a931 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/srcset/invalid.html.prettier-snap @@ -0,0 +1,20 @@ + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html new file mode 100644 index 000000000000..6934c58a7604 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html @@ -0,0 +1,29 @@ + + + + SVG + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.prettier-snap new file mode 100644 index 000000000000..4284683216dd --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.prettier-snap @@ -0,0 +1,30 @@ + + + + SVG + + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.snap new file mode 100644 index 000000000000..b9a400bf3c8e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.html.snap @@ -0,0 +1,128 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/svg/embeded/svg.html +--- +# Input + +```html + + + + SVG + + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + SVG +@@ -6,25 +6,24 @@ + + + ++document.addEventListener( ++'DOMContentLoaded', () => { ++ const element = document.getElementById('foo') ++ if (element) { ++element.fillStyle = 'currentColor' ++} ++}); ++ + ++ div { ++ color: white; ++ font:18px serif; ++ height: 100%; ++ overflow: auto; ++ } ++ + + + +``` + +# Output + +```html + + + + SVG + + + + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg new file mode 100644 index 000000000000..2a86602fb068 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg @@ -0,0 +1,21 @@ + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg.prettier-snap new file mode 100644 index 000000000000..f26bd356420e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/embeded/svg.svg.prettier-snap @@ -0,0 +1,22 @@ + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html new file mode 100644 index 000000000000..21f7256f3f80 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html @@ -0,0 +1,35 @@ + + + + SVG + + + + + + + + + + + + + + Text + + + + + +
    +

    + 123 +

    + + 123 + +
    +
    +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.prettier-snap new file mode 100644 index 000000000000..7ae9c90af5ee --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.prettier-snap @@ -0,0 +1,40 @@ + + + + SVG + + + + + + + + + + + + + + + Text + + + + + + +
    +

    123

    + 123 +
    +
    +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.snap new file mode 100644 index 000000000000..1dc29cf42cce --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/svg/svg.html.snap @@ -0,0 +1,117 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/svg/svg.html +--- +# Input + +```html + + + + SVG + + + + + + + + + + + + + + Text + + + + + +
    +

    + 123 +

    + + 123 + +
    +
    +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + SVG +@@ -34,7 +34,7 @@ + --> +
    +

    123

    +- 123 ++ 123 +
    + + +``` + +# Output + +```html + + + + SVG + + + + + + + + + + + + + + + Text + + + + + + +
    +

    123

    + 123 +
    +
    +
    +``` + +# Lines exceeding max width of 80 characters +``` + 33: In the context of SVG embeded into HTML, the XHTML namespace could be avoided, but it is mandatory in the context of an SVG document +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html b/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html new file mode 100644 index 000000000000..4ba276d56889 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html @@ -0,0 +1,4 @@ +

    I will display €

    +

    I will display !

    +

    I will display €

    +

    I will display €

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html.prettier-snap new file mode 100644 index 000000000000..4ba276d56889 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/symbol_entities/symbol_entitites.html.prettier-snap @@ -0,0 +1,4 @@ +

    I will display €

    +

    I will display !

    +

    I will display €

    +

    I will display €

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html new file mode 100644 index 000000000000..4b5b509da5c5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html @@ -0,0 +1 @@ +hello world diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html.prettier-snap new file mode 100644 index 000000000000..4b5b509da5c5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/case-sensitive.html.prettier-snap @@ -0,0 +1 @@ +hello world diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html new file mode 100644 index 000000000000..ca3c54d84de9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html @@ -0,0 +1,15 @@ +
    + aaaaaaaaaa + bbbbbbbbbb + cccccccccc +
    +
    + aaaaaaaaaa + bbbbbbbbbbcccccccccc +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html.prettier-snap new file mode 100644 index 000000000000..50e690a0dd06 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/closing-at-start.html.prettier-snap @@ -0,0 +1,15 @@ +
    + aaaaaaaaaa + bbbbbbbbbb + cccccccccc +
    +
    + aaaaaaaaaa + bbbbbbbbbbcccccccccc +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html new file mode 100644 index 000000000000..c430869731c8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html @@ -0,0 +1,2 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.prettier-snap new file mode 100644 index 000000000000..c430869731c8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.prettier-snap @@ -0,0 +1,2 @@ + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.snap new file mode 100644 index 000000000000..e2ece463c6ad --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/custom-element.html.snap @@ -0,0 +1,31 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/custom-element.html +--- +# Input + +```html + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,2 +1,2 @@ +- +- ++ ++ +``` + +# Output + +```html + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html new file mode 100644 index 000000000000..94e19d673cc2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html @@ -0,0 +1,12 @@ +This text will scroll from right to left + +This text will scroll from bottom to top + + + This text will bounce + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.prettier-snap new file mode 100644 index 000000000000..89d9534119de --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.prettier-snap @@ -0,0 +1,13 @@ +This text will scroll from right to left + +This text will scroll from bottom to top + + + This text will bounce + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.snap new file mode 100644 index 000000000000..a3f92ff852e8 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/marquee.html.snap @@ -0,0 +1,57 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/marquee.html +--- +# Input + +```html +This text will scroll from right to left + +This text will scroll from bottom to top + + + This text will bounce + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -7,7 +7,7 @@ + width="250" + height="200" + behavior="alternate" +- style="border: solid" ++ style="border:solid" + > +- This text will bounce ++ This text will bounce + +``` + +# Output + +```html +This text will scroll from right to left + +This text will scroll from bottom to top + + + This text will bounce + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html new file mode 100644 index 000000000000..edd87a9fe110 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html @@ -0,0 +1,5 @@ + +
  • +
  • +
  • +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.prettier-snap new file mode 100644 index 000000000000..edd87a9fe110 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.prettier-snap @@ -0,0 +1,5 @@ + +
  • +
  • +
  • +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.snap new file mode 100644 index 000000000000..c2c7d0f1ce61 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/menu.html.snap @@ -0,0 +1,53 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/menu.html +--- +# Input + +```html + +
  • +
  • +
  • +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1,11 @@ + +-
  • +-
  • +-
  • ++
  • ++ ++
  • ++
  • ++ ++
  • ++
  • ++ ++
  • +
    +``` + +# Output + +```html + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html new file mode 100644 index 000000000000..111374a53b0b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html @@ -0,0 +1,29 @@ +

    Want to write us a letter? Use ourmailing address.

    + +

    Want to write us a letter? Use ourmailing address.

    + +

    Want to write us a letter? Use ourmailing address.

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.prettier-snap new file mode 100644 index 000000000000..917acb51392c --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.prettier-snap @@ -0,0 +1,22 @@ +

    + Want to write us a letter? Use ourmailing address. +

    + +

    + Want to write us a letter? Use ourmailing address. +

    + +

    + Want to write us a letter? Use ourmailing address. +

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.snap new file mode 100644 index 000000000000..2db4e9b4a5f4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/openging-at-end.html.snap @@ -0,0 +1,108 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/openging-at-end.html +--- +# Input + +```html +

    Want to write us a letter? Use ourmailing address.

    + +

    Want to write us a letter? Use ourmailing address.

    + +

    Want to write us a letter? Use ourmailing address.

    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,12 +1,16 @@ +

    + Want to write us a letter? Use ourmailing addressmailing address. +

    + +

    + Want to write us a letter? Use ourmailing addressmailing address. +

    + +@@ -17,6 +21,8 @@ + href2="contacts.html#Mailing_address" + href3="contacts.html#Mailing_address" + href4="contacts.html#Mailing_address" +- >mailing addressmailing address. +

    +``` + +# Output + +```html +

    + Want to write us a letter? Use ourmailing address. +

    + +

    + Want to write us a letter? Use ourmailing address. +

    + +

    + Want to write us a letter? Use ourmailing address. +

    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html new file mode 100644 index 000000000000..1c308258cfa9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html @@ -0,0 +1,3 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html.prettier-snap new file mode 100644 index 000000000000..4520b205e12d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/option.html.prettier-snap @@ -0,0 +1,13 @@ + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html new file mode 100644 index 000000000000..0c026accdfc0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html @@ -0,0 +1,101 @@ +
    +--------------------------------------------------------------------------------
    +
    +
    +                                      *         *       *
    +                                     **        **      ***
    +                                     **        **       *
    +   ****    ***  ****               ********  ********                   ***  ****
    +  * ***  *  **** **** *    ***    ********  ********  ***        ***     **** **** *
    + *   ****    **   ****    * ***      **        **      ***      * ***     **   ****
    +**    **     **          *   ***     **        **       **     *   ***    **
    +**    **     **         **    ***    **        **       **    **    ***   **
    +**    **     **         ********     **        **       **    ********    **
    +**    **     **         *******      **        **       **    *******     **
    +**    **     **         **           **        **       **    **          **
    +*******      ***        ****    *    **        **       **    ****    *   ***
    +******        ***        *******      **        **      *** *  *******     ***
    +**                        *****                          ***    *****
    +**
    +**
    + **
    +
    +--------------------------------------------------------------------------------
    +
    +
    +
    +        Text in a pre element
    +
    +    is displayed in a fixed-width
    +
    +   font, and it preserves
    +
    +   both             spaces and
    +
    +   line breaks
    +
    +
    +
         Foo     Bar     
    +
    +     Foo     Bar
    +
    +
    Foo     Bar
    +
    +
    +     Foo     Bar
    +
    +
    +___________________________
    +< I'm an expert in my field. >
    +---------------------------
    +     \   ^__^
    +      \  (oo)\_______
    +         (__)\       )\/\
    +             ||----w |
    +             ||     ||
    +___________________________
    +  
    +
    + A cow saying, "I'm an expert in my field." The cow is illustrated using preformatted text characters. +
    +
    +
    +     Foo     Bar
    +
    +
    +
    +
    +
    +
    +          ______
    +          STRING
    +          ______
    +        
    +
    +
    +
    +
    +
    
    +
    +
    + +
    +
    
    +  
    + +
    +  
    +
    +
    + + +

    +

    +

    +

    +

    +

    +
    +
    +

    long long long text long long long text long long long text long long long text
    +

    long long long text long long long text long long long text long long long text
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.prettier-snap new file mode 100644 index 000000000000..9e9a27a7e3ee --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.prettier-snap @@ -0,0 +1,111 @@ +
    +--------------------------------------------------------------------------------
    +
    +
    +                                      *         *       *
    +                                     **        **      ***
    +                                     **        **       *
    +   ****    ***  ****               ********  ********                   ***  ****
    +  * ***  *  **** **** *    ***    ********  ********  ***        ***     **** **** *
    + *   ****    **   ****    * ***      **        **      ***      * ***     **   ****
    +**    **     **          *   ***     **        **       **     *   ***    **
    +**    **     **         **    ***    **        **       **    **    ***   **
    +**    **     **         ********     **        **       **    ********    **
    +**    **     **         *******      **        **       **    *******     **
    +**    **     **         **           **        **       **    **          **
    +*******      ***        ****    *    **        **       **    ****    *   ***
    +******        ***        *******      **        **      *** *  *******     ***
    +**                        *****                          ***    *****
    +**
    +**
    + **
    +
    +--------------------------------------------------------------------------------
    +
    +
    +
    +        Text in a pre element
    +
    +    is displayed in a fixed-width
    +
    +   font, and it preserves
    +
    +   both             spaces and
    +
    +   line breaks
    +
    +
    +
         Foo     Bar     
    +
    +     Foo     Bar
    +
    +
    +Foo     Bar
    +
    +
         Foo     Bar
    +
    +
    +___________________________
    +< I'm an expert in my field. >
    +---------------------------
    +     \   ^__^
    +      \  (oo)\_______
    +         (__)\       )\/\
    +             ||----w |
    +             ||     ||
    +___________________________
    +  
    +
    + A cow saying, "I'm an expert in my field." The cow is illustrated using + preformatted text characters. +
    +
    +
    +     Foo     Bar
    +
    +
    +
    +
    +
    +
    +          ______
    +          STRING
    +          ______
    +        
    +
    +
    +
    +
    +
    
    +
    +
    + +
    +
    
    +  
    +
    + +
    +
    +  
    +
    +
    + + +

    +

    +

    +

    +

    +

    +
    +
    +

    long long long text long long long text long long long text long long long text
    +

    long long long text long long long text long long long text long long long text
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.snap new file mode 100644 index 000000000000..595008d2b767 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/pre.html.snap @@ -0,0 +1,280 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/pre.html +--- +# Input + +```html +
    +--------------------------------------------------------------------------------
    +
    +
    +                                      *         *       *
    +                                     **        **      ***
    +                                     **        **       *
    +   ****    ***  ****               ********  ********                   ***  ****
    +  * ***  *  **** **** *    ***    ********  ********  ***        ***     **** **** *
    + *   ****    **   ****    * ***      **        **      ***      * ***     **   ****
    +**    **     **          *   ***     **        **       **     *   ***    **
    +**    **     **         **    ***    **        **       **    **    ***   **
    +**    **     **         ********     **        **       **    ********    **
    +**    **     **         *******      **        **       **    *******     **
    +**    **     **         **           **        **       **    **          **
    +*******      ***        ****    *    **        **       **    ****    *   ***
    +******        ***        *******      **        **      *** *  *******     ***
    +**                        *****                          ***    *****
    +**
    +**
    + **
    +
    +--------------------------------------------------------------------------------
    +
    +
    +
    +        Text in a pre element
    +
    +    is displayed in a fixed-width
    +
    +   font, and it preserves
    +
    +   both             spaces and
    +
    +   line breaks
    +
    +
    +
         Foo     Bar     
    +
    +     Foo     Bar
    +
    +
    Foo     Bar
    +
    +
    +     Foo     Bar
    +
    +
    +___________________________
    +< I'm an expert in my field. >
    +---------------------------
    +     \   ^__^
    +      \  (oo)\_______
    +         (__)\       )\/\
    +             ||----w |
    +             ||     ||
    +___________________________
    +  
    +
    + A cow saying, "I'm an expert in my field." The cow is illustrated using preformatted text characters. +
    +
    +
    +     Foo     Bar
    +
    +
    +
    +
    +
    +
    +          ______
    +          STRING
    +          ______
    +        
    +
    +
    +
    +
    +
    
    +
    +
    + +
    +
    
    +  
    + +
    +  
    +
    +
    + + +

    +

    +

    +

    +

    +

    +
    +
    +

    long long long text long long long text long long long text long long long text
    +

    long long long text long long long text long long long text long long long text
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -39,10 +39,10 @@ +
    +      Foo     Bar
    + 
    +-
    +-Foo     Bar
    ++
    Foo     Bar
    + 
    +-
         Foo     Bar
    ++
    ++     Foo     Bar
    +
    +
    + ___________________________
    +@@ -100,12 +100,14 @@
    + 
    + 
    + 

    +-

    ++

    +

    +-

    ++

    +

    +-

    ++

    +
    +-
    +-

    long long long text long long long text long long long text long long long text
    +-

    long long long text long long long text long long long text long long long text
    ++
    ++

    long long long text long long long text long long long text long long long text
    ++

    long long long text long long long text long long long text long long long text
    +``` + +# Output + +```html +
    +--------------------------------------------------------------------------------
    +
    +
    +                                      *         *       *
    +                                     **        **      ***
    +                                     **        **       *
    +   ****    ***  ****               ********  ********                   ***  ****
    +  * ***  *  **** **** *    ***    ********  ********  ***        ***     **** **** *
    + *   ****    **   ****    * ***      **        **      ***      * ***     **   ****
    +**    **     **          *   ***     **        **       **     *   ***    **
    +**    **     **         **    ***    **        **       **    **    ***   **
    +**    **     **         ********     **        **       **    ********    **
    +**    **     **         *******      **        **       **    *******     **
    +**    **     **         **           **        **       **    **          **
    +*******      ***        ****    *    **        **       **    ****    *   ***
    +******        ***        *******      **        **      *** *  *******     ***
    +**                        *****                          ***    *****
    +**
    +**
    + **
    +
    +--------------------------------------------------------------------------------
    +
    +
    +
    +        Text in a pre element
    +
    +    is displayed in a fixed-width
    +
    +   font, and it preserves
    +
    +   both             spaces and
    +
    +   line breaks
    +
    +
    +
         Foo     Bar     
    +
    +     Foo     Bar
    +
    +
    Foo     Bar
    +
    +
    +     Foo     Bar
    +
    +
    +___________________________
    +< I'm an expert in my field. >
    +---------------------------
    +     \   ^__^
    +      \  (oo)\_______
    +         (__)\       )\/\
    +             ||----w |
    +             ||     ||
    +___________________________
    +  
    +
    + A cow saying, "I'm an expert in my field." The cow is illustrated using + preformatted text characters. +
    +
    +
    +     Foo     Bar
    +
    +
    +
    +
    +
    +
    +          ______
    +          STRING
    +          ______
    +        
    +
    +
    +
    +
    +
    
    +
    +
    + +
    +
    
    +  
    +
    + +
    +
    +  
    +
    +
    + + +

    +

    +

    +

    +

    +

    +
    +
    +

    long long long text long long long text long long long text long long long text
    +

    long long long text long long long text long long long text long long long text
    +``` + +# Lines exceeding max width of 80 characters +``` + 8: **** *** **** ******** ******** *** **** + 9: * *** * **** **** * *** ******** ******** *** *** **** **** * + 10: * **** ** **** * *** ** ** *** * *** ** **** + 111: >
    long long long text long long long text long long long text long long long text
    + 113: >
    long long long text long long long text long long long text long long long text
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html new file mode 100644 index 000000000000..fb21682974c2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html @@ -0,0 +1,24 @@ + + + + + + + Document + + +
    +

    My fancy blog

    + ... + +
    + + + +
    +
    + + +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.prettier-snap new file mode 100644 index 000000000000..399f1e4a1149 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.prettier-snap @@ -0,0 +1,24 @@ + + + + + + + Document + + +
    +

    My fancy blog

    + ... + +
    + + + +
    +
    + + +
    + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.snap new file mode 100644 index 000000000000..05128ab4a380 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/seach.html.snap @@ -0,0 +1,98 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/seach.html +--- +# Input + +```html + + + + + + + Document + + +
    +

    My fancy blog

    + ... + +
    + + + +
    +
    + + +
    + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,4 +1,4 @@ +- ++ + + + +@@ -8,7 +8,9 @@ + + +
    +-

    My fancy blog

    ++

    ++ My fancy blog ++

    + ... + +
    +@@ -18,7 +20,7 @@ +
    +
    + +- ++ +
    + + +``` + +# Output + +```html + + + + + + + Document + + +
    +

    + My fancy blog +

    + ... + +
    + + + +
    +
    + + +
    + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html new file mode 100644 index 000000000000..085d81c13774 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html @@ -0,0 +1,125 @@ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    +
    string
    +
    string
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    + +
    string
    +
    string
    string
    +
    string
    string
    +
    string
    string
    +
    +
    +
    string
    +
    +
    string
    +
    +
    + +
    string
    + +
    +
    + +
    string
    + +
    string
    + +
    +
      123
    • First
    • 456
    • Second
    • 789
    +*200 +123 +
    123456
    +

    x

    +

    x

    +

    x

    + + + | + +
    +

    + + +

    +

    + + +

    +

    "" is the property bound title.

    +
  • 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  • +
    + + + + + + + + + + + +
    +tag name in other namespace should also lower cased +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + "seddoeiusmod". +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + seddoeiusmod. +
    + + + + + + + + + +
    Should not insert empty line before this div
    + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.prettier-snap new file mode 100644 index 000000000000..92d81e5e3722 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.prettier-snap @@ -0,0 +1,197 @@ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    + very very very very very very very very very very very very very very very + very long string +
    +
    + string +
    +
    + string +
    +
    + string +
    +
    + very very very very very very very very very very very very very very very + very long string +
    +
    + string +
    +
    + very very very very very very very very very very very very very very very + very long string +
    + +
    string
    +
    +
    string
    +
    string
    +
    +
    +
    string
    +
    string
    +
    +
    +
    string
    +
    string
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    +
    +
    +
    +
    +
    +
    string
    +
    +
    +
    string
    +
    +
    +
    string
    + +
    string
    +
    +
      + 123 +
    • First
    • + 456 +
    • Second
    • + 789 +
    +*200 +123 +
    123456
    +

    x

    +

    x

    +

    x

    + + + +| + +
    + +

    + + +

    + +

    + + +

    +

    "" is the property bound title.

    +
  • + 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +
  • +
    + + + + + + + + + + + +
    +tag name in other namespace should also lower cased +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + "seddoeiusmod". +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + seddoeiusmod. +
    + + + + + + + + + +
    +
    Should not insert empty line before this div
    + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.snap new file mode 100644 index 000000000000..d321a62a8ba4 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags.html.snap @@ -0,0 +1,563 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/tags.html +--- +# Input + +```html +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    +
    string
    +
    string
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    +
    string
    +
    very very very very very very very very very very very very very very very very long string
    + +
    string
    +
    string
    string
    +
    string
    string
    +
    string
    string
    +
    +
    +
    string
    +
    +
    string
    +
    +
    + +
    string
    + +
    +
    + +
    string
    + +
    string
    + +
    +
      123
    • First
    • 456
    • Second
    • 789
    +*200 +123 +
    123456
    +

    x

    +

    x

    +

    x

    + + + | + +
    +

    + + +

    +

    + + +

    +

    "" is the property bound title.

    +
  • 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  • +
    + + + + + + + + + + + +
    +tag name in other namespace should also lower cased +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + "seddoeiusmod". +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + seddoeiusmod. +
    + + + + + + + + + +
    Should not insert empty line before this div
    + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -72,20 +72,28 @@ + + Your browser does not support the video tag. + +-
    string
    +
    +
    string
    ++
    ++
    ++
    string
    +
    string
    +
    +
    +-
    string
    ++
    ++
    string
    ++
    +
    string
    +
    +
    +
    string
    +-
    string
    ++
    ++
    string
    ++
    ++
    ++
    ++
    +
    +-
    +
    +
    +
    +@@ -95,7 +103,9 @@ +
    +
    +
    +-
    string
    ++
    ++
    string
    ++
    +
    +
    +
    +@@ -113,65 +123,80 @@ +
    string
    +
    +
      +- 123 +-
    • First
    • +- 456 +-
    • Second
    • +- 789 ++ 123
    • First
    • 456
    • Second
    • 789 +
    +-*200 ++*200 + 123 +-
    123456
    +-

    x

    +-

    x

    +-

    x

    ++/> ++123 ++
    ++ 123 ++ ++ 456 ++
    ++

    ++ x ++

    ++

    ++ x ++

    ++

    ++ x ++

    + + + +-| +- ++| +
    + +-

    ++
    ++
    + + +-

    ++
    ++
    + +-

    ++
    ++
    + + +-

    +-

    "" is the property bound title.

    ++
    ++
    ++

    ++ "" is the property boundtitle. ++

    +
  • + 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +
  • +
    +- +- +- ++ ++ ++ + +- +- +- ++ ++ ++ + +- +- +- ++ ++ ++ +
    +-tag name in other namespace should also lower cased ++ ++ tag name in other namespace should also lower cased ++ +
    +- Lorem ipsum dolor sit amet, consectetur adipiscing elit, +- "seddoeiusmod". ++ Lorem ipsum dolor sit amet, consectetur adipiscing elit, "seddoeiusmod". +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, +@@ -190,8 +215,14 @@ +
    Should not insert empty line before this div
    + + +- + +- ++ > ++ ++ ++ +``` + +# Output + +```html +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    + very very very very very very very very very very very very very very very + very long string +
    +
    + string +
    +
    + string +
    +
    + string +
    +
    + very very very very very very very very very very very very very very very + very long string +
    +
    + string +
    +
    + very very very very very very very very very very very very very very very + very long string +
    + +
    +
    string
    +
    +
    +
    string
    +
    string
    +
    +
    +
    +
    string
    +
    +
    string
    +
    +
    +
    string
    +
    +
    string
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    +
    +
    +
    +
    +
    +
    +
    string
    +
    +
    +
    string
    +
    +
    +
    string
    + +
    string
    +
    +
      + 123
    • First
    • 456
    • Second
    • 789 +
    +*200 + +123 +
    + 123 + + 456 +
    +

    + x +

    +

    + x +

    +

    + x +

    + + + +| +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    +

    + "" is the property boundtitle. +

    +
  • + 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +
  • +
    + + + + + + + + + + + +
    + + tag name in other namespace should also lower cased + +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, "seddoeiusmod". +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + seddoeiusmod. +
    + + + + + + + + + +
    +
    Should not insert empty line before this div
    + + + + + + +``` + +# Lines exceeding max width of 80 characters +``` + 7: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-attribute + 12: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-attribute="value" + 15: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-attribute="very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value" + 31: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-attribute + 36: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-attribute="value" + 41: attribute="very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value" + 132: src="longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong" + 178: 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html new file mode 100644 index 000000000000..f9cc62d91ec6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html @@ -0,0 +1,11 @@ +
    beforeafter
    + +
    before
    summary long long long long details
    after
    + +
    beforedialog long long long long long long long long after
    + +
    beforeafter
    + +
    beforeafter
    + +
    beforeafter
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.prettier-snap new file mode 100644 index 000000000000..35a8b858b2c6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.prettier-snap @@ -0,0 +1,40 @@ +
    + beforeafter +
    + +
    + before +
    + summary long long long long + details +
    + after +
    + +
    + before + dialog long long long long long long long long + after +
    + +
    + before + + after +
    + +
    + beforeafter +
    + +
    beforeafter
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.snap new file mode 100644 index 000000000000..81ccf5a9bd4d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/tags2.html.snap @@ -0,0 +1,125 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/tags2.html +--- +# Input + +```html +
    beforeafter
    + +
    before
    summary long long long long details
    after
    + +
    beforedialog long long long long long long long long after
    + +
    beforeafter
    + +
    beforeafter
    + +
    beforeafter
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,40 +1,36 @@ +
    +- beforeafter ++ before ++ after +
    + +
    + before +
    +- summary long long long long +- details ++ summary long long long long details +
    + after +
    + +
    +- before +- dialog long long long long long long long long ++ beforedialog long long long long long long long long + after +
    + +
    +- before ++ before ++ ++ + +- after ++ ++ after +
    + +
    +- beforeafter ++ before ++ ++ after +
    + +-
    beforeafter
    ++
    ++ beforeafter ++
    +``` + +# Output + +```html +
    + before + after +
    + +
    + before +
    + summary long long long long details +
    + after +
    + +
    + beforedialog long long long long long long long long + after +
    + +
    + before + + + + + after +
    + +
    + before + + after +
    + +
    + beforeafter +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html new file mode 100644 index 000000000000..11c5e41144da --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html @@ -0,0 +1,30 @@ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.prettier-snap new file mode 100644 index 000000000000..11c5e41144da --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.prettier-snap @@ -0,0 +1,30 @@ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.snap new file mode 100644 index 000000000000..213f74754965 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/textarea.html.snap @@ -0,0 +1,101 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/tags/textarea.html +--- +# Input + +```html +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -11,7 +11,7 @@ +
    +
    + +
    +
    +@@ -27,4 +27,6 @@ +
    + + +-
    ++
    ++ ++
    +``` + +# Output + +```html +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html b/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html new file mode 100644 index 000000000000..98d2a9bbb229 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html @@ -0,0 +1 @@ +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html.prettier-snap new file mode 100644 index 000000000000..98d2a9bbb229 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/tags/unsupported.html.prettier-snap @@ -0,0 +1 @@ +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html new file mode 100644 index 000000000000..77085766bccf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html @@ -0,0 +1,4 @@ +foo bar foo bar + foo bar foo bar foo bar foo bar foo bar + foo bar foo bar + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.prettier-snap new file mode 100644 index 000000000000..9ef6880e73b9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.prettier-snap @@ -0,0 +1,5 @@ +foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo + bar + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap new file mode 100644 index 000000000000..1f135072c93f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/text/tag-should-in-fill.html.snap @@ -0,0 +1,43 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/text/tag-should-in-fill.html +--- +# Input + +```html +foo bar foo bar + foo bar foo bar foo bar foo bar foo bar + foo bar foo bar + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1,4 @@ +-foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo +- barfoo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + +``` + +# Output + +```html +foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + +``` + +# Lines exceeding max width of 80 characters +``` + 2: >foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo barLorem, ispum dolor sit amet. +
    Lorem, ispum dolor sit amet.
    +
    Lorem, ispum dolor sit amet.
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.prettier-snap new file mode 100644 index 000000000000..25c258501daa --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.prettier-snap @@ -0,0 +1,5 @@ +Lorem, ispum dolor sit amet. +
    Lorem, ispum dolor sit amet.
    +
    +
    Lorem, ispum dolor sit amet.
    +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.snap new file mode 100644 index 000000000000..d9a7da365932 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/break-tags.html.snap @@ -0,0 +1,46 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/break-tags.html +--- +# Input + +```html +Lorem, ispum dolor sit amet. +
    Lorem, ispum dolor sit amet.
    +
    Lorem, ispum dolor sit amet.
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,5 +1,9 @@ + Lorem, ispum dolor sit amet. +-
    Lorem, ispum dolor sit amet.
    +
    +-
    Lorem, ispum dolor sit amet.
    ++ Lorem, ispum dolor sit amet. ++
    ++
    ++
    ++ Lorem, ispum dolor sit amet. ++
    +
    +``` + +# Output + +```html +Lorem, ispum dolor sit amet. +
    + Lorem, ispum dolor sit amet. +
    +
    +
    + Lorem, ispum dolor sit amet. +
    +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html new file mode 100644 index 000000000000..dbfd8c45f36d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html @@ -0,0 +1,12 @@ + + +
    + +
    +
    + + +
    + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.prettier-snap new file mode 100644 index 000000000000..6fc0e479782b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.prettier-snap @@ -0,0 +1,25 @@ + + +
    + +
    +
    + + +
    + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap new file mode 100644 index 000000000000..f6223410fb79 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap @@ -0,0 +1,83 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/display-inline-block.html +--- +# Input + +```html + + +
    + +
    +
    + + +
    + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -6,10 +6,11 @@ + +
    + ++ +
    +
    + +
    + +``` + +# Output + +```html + + +
    + + +
    +
    + + +
    + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html new file mode 100644 index 000000000000..f87a42f6a6cf --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html @@ -0,0 +1 @@ +My tITlE diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.prettier-snap new file mode 100644 index 000000000000..ae6400f3eec9 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.prettier-snap @@ -0,0 +1,8 @@ + + + + + My tITlE + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.snap new file mode 100644 index 000000000000..f540515e35d6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-none.html.snap @@ -0,0 +1,48 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/display-none.html +--- +# Input + +```html +My tITlE + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,8 +1,8 @@ +- +- +- +- +- My tITlE +- +- +- ++ ++ ++ ++ ++ My tITlE ++ ++ ++ +``` + +# Output + +```html + + + + + My tITlE + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html new file mode 100644 index 000000000000..3a475a4263da --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html @@ -0,0 +1,11 @@ +

    + about fedco bottom imageWe are a cooperative, one of the few seed companies so organized + in the United States. Because we do not have an individual owner or beneficiary, + profit is not our primary goal. Consumers own 60% of the cooperative and worker + members 40%. Consumer and worker members share proportionately in the cooperative’s + profits through our annual patronage dividends. +

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.prettier-snap new file mode 100644 index 000000000000..6dbc89e4756b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.prettier-snap @@ -0,0 +1,12 @@ +

    + about fedco bottom imageWe are a cooperative, one of the few seed companies so + organized in the United States. Because we do not have an individual owner or + beneficiary, profit is not our primary goal. Consumers own 60% of the + cooperative and worker members 40%. Consumer and worker members share + proportionately in the cooperative’s profits through our annual + patronage dividends. +

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.snap new file mode 100644 index 000000000000..9ab14061de14 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/fill.html.snap @@ -0,0 +1,58 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/fill.html +--- +# Input + +```html +

    + about fedco bottom imageWe are a cooperative, one of the few seed companies so organized + in the United States. Because we do not have an individual owner or beneficiary, + profit is not our primary goal. Consumers own 60% of the cooperative and worker + members 40%. Consumer and worker members share proportionately in the cooperative’s + profits through our annual patronage dividends. +

    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -2,8 +2,9 @@ + about fedco bottom imageWe are a cooperative, one of the few seed companies so ++ style="float: left;" ++ /> ++ We are a cooperative, one of the few seed companies so + organized in the United States. Because we do not have an individual owner or + beneficiary, profit is not our primary goal. Consumers own 60% of the + cooperative and worker members 40%. Consumer and worker members share +``` + +# Output + +```html +

    + about fedco bottom image + We are a cooperative, one of the few seed companies so + organized in the United States. Because we do not have an individual owner or + beneficiary, profit is not our primary goal. Consumers own 60% of the + cooperative and worker members 40%. Consumer and worker members share + proportionately in the cooperative’s profits through our annual + patronage dividends. +

    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html new file mode 100644 index 000000000000..429d011d91e3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html @@ -0,0 +1,3 @@ + 321 + + 321 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.prettier-snap new file mode 100644 index 000000000000..429d011d91e3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.prettier-snap @@ -0,0 +1,3 @@ + 321 + + 321 diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.snap new file mode 100644 index 000000000000..17d29fa79544 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-leading-trailing-spaces.html.snap @@ -0,0 +1,38 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/inline-leading-trailing-spaces.html +--- +# Input + +```html + 321 + + 321 + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,3 +1,5 @@ +- 321 ++321 + +- 321 ++ ++ 321 ++ +``` + +# Output + +```html +321 + + + 321 + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html new file mode 100644 index 000000000000..e5a8a8317ec2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html @@ -0,0 +1,15 @@ +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue +vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. +Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. +Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. +Sed eu scelerisque neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, vehicula +euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per +conubia nostra, per inceptos himenaeos. Donec in ornare velit.

    + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue +vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. +Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. +Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. +Sed eu scelerisque neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, vehicula +euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per +conubia nostra, per inceptos himenaeos. Donec in ornare velit.

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.prettier-snap new file mode 100644 index 000000000000..402bf6d787f3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.prettier-snap @@ -0,0 +1,23 @@ +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa + vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque + neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, + vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti + sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec + in ornare velit. +

    + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa + vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque + neque. Donec maximus rhoncus pellentesque. Aenean purus + turpis, vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti + sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec + in ornare velit. +

    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.snap new file mode 100644 index 000000000000..7c7b8e6d651f --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/inline-nodes.html.snap @@ -0,0 +1,92 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/inline-nodes.html +--- +# Input + +```html +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue +vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. +Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. +Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. +Sed eu scelerisque neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, vehicula +euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per +conubia nostra, per inceptos himenaeos. Donec in ornare velit.

    + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue +vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. +Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. +Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. +Sed eu scelerisque neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, vehicula +euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per +conubia nostra, per inceptos himenaeos. Donec in ornare velit.

    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -4,10 +4,10 @@ + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque +- neque. Donec maximus rhoncus pellentesque. Aenean purus turpis, +- vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti +- sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec +- in ornare velit. ++ neque. Donec maximusrhoncus pellentesque. Aenean purus turpis, vehicula ++ euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad ++ litora torquent per conubia nostra, per inceptos himenaeos. Donec in ornare ++ velit. +

    + +

    +@@ -16,8 +16,10 @@ + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque +- neque. Donec maximus rhoncus pellentesque. Aenean purus +- turpis, vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti +- sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec +- in ornare velit. ++ neque. Donec ++ maximusrhoncus pellentesque. Aenean purus turpis, vehicula euismod ante vel, ++ ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per ++ conubia nostra, per inceptos himenaeos. Donec in ornare velit. +

    +``` + +# Output + +```html +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa + vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque + neque. Donec maximusrhoncus pellentesque. Aenean purus turpis, vehicula + euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Donec in ornare + velit. +

    + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa + vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet + urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed + ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse + vitae odio vitae massa hendrerit mattis sed eget dui. Sed eu scelerisque + neque. Donec + maximusrhoncus pellentesque. Aenean purus turpis, vehicula euismod ante vel, + ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per + conubia nostra, per inceptos himenaeos. Donec in ornare velit. +

    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html new file mode 100644 index 000000000000..824b0bf9df8b --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html @@ -0,0 +1,10 @@ +/ˌɪləˈnɔɪ/ + +ipsum diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.prettier-snap new file mode 100644 index 000000000000..e14a6292748d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.prettier-snap @@ -0,0 +1,22 @@ +/ˌɪləˈnɔɪ/ + +ipsum diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap new file mode 100644 index 000000000000..bb1dbc5c6cf6 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/nested-inline-without-whitespace.html.snap @@ -0,0 +1,93 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/nested-inline-without-whitespace.html +--- +# Input + +```html +/ˌɪləˈnɔɪ/ + +ipsum + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,22 +1,23 @@ + /ˌɪləˈnɔɪ/ˌ ++ ɪ ++ l ++ ə ++ ˈ ++ n ++ ɔɪ/ + + ipsumi ++ p ++ s ++ u ++ m +``` + +# Output + +```html +/ˌ + ɪ + l + ə + ˈ + n + ɔɪ/ + +i + p + s + u + m +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html new file mode 100644 index 000000000000..55a8b9477611 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html @@ -0,0 +1,6 @@ + +Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores voluptas quaerat ut qui sunt vitae error. + +Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores voluptas quaerat ut qui sunt vitae error. + +Prix : 32 € diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html.prettier-snap new file mode 100644 index 000000000000..a189a0240a8a --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/non-breaking-whitespace.html.prettier-snap @@ -0,0 +1,11 @@ + +Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores + voluptas quaerat ut qui sunt vitae error. + +Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores voluptas quaerat ut qui sunt vitae error. + +Prix : 32 € diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html new file mode 100644 index 000000000000..06862ebaa584 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html @@ -0,0 +1,17 @@ +123 + +123 +123 + + +123 + + +
    123
    +
    +123
    +
    123 +
    +
    +123 +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.prettier-snap new file mode 100644 index 000000000000..34a12ee2ba10 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.prettier-snap @@ -0,0 +1,9 @@ +123 + 123 +123 + 123 + +
    123
    +
    123
    +
    123
    +
    123
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.snap new file mode 100644 index 000000000000..84890adfa97e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/surrounding-linebreak.html.snap @@ -0,0 +1,59 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/surrounding-linebreak.html +--- +# Input + +```html +123 + +123 +123 + + +123 + + +
    123
    +
    +123
    +
    123 +
    +
    +123 +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,7 +1,7 @@ + 123 +- 123 +-123 +- 123 ++123 ++123 ++123 + +
    123
    +
    123
    +``` + +# Output + +```html +123 +123 +123 +123 + +
    123
    +
    123
    +
    123
    +
    123
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html new file mode 100644 index 000000000000..c161476daa7e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html @@ -0,0 +1,20 @@ + + + + + + + + +
    ABC
    + +
    ABC
    + +
    A B C
    + + + + + + +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.prettier-snap new file mode 100644 index 000000000000..999326f187f1 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.prettier-snap @@ -0,0 +1,35 @@ + + + + + + + + +
    ABC
    + + + + + + + + + +
    ABC
    + + + + + + + + + +
    ABC
    + + + + + +
    diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.snap new file mode 100644 index 000000000000..bc4cbf926304 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/table.html.snap @@ -0,0 +1,90 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/table.html +--- +# Input + +```html + + + + + + + + +
    ABC
    + +
    ABC
    + +
    A B C
    + + + + + + +
    + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -21,9 +21,9 @@ + + + +- +- +- ++ ++ ++ + + +
    ABCA B C
    +``` + +# Output + +```html + + + + + + + + +
    ABC
    + + + + + + + + + +
    ABC
    + + + + + + + + + +
    A B C
    + + + + + +
    +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html new file mode 100644 index 000000000000..c1910c0e82c5 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html @@ -0,0 +1,7 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.prettier-snap new file mode 100644 index 000000000000..5a7afad15041 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.prettier-snap @@ -0,0 +1,9 @@ + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.snap new file mode 100644 index 000000000000..dd776a701c08 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/template.html.snap @@ -0,0 +1,49 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/whitespace/template.html +--- +# Input + +```html + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -3,7 +3,7 @@ + + + +``` + +# Output + +```html + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html new file mode 100644 index 000000000000..358b9ca75691 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html @@ -0,0 +1,8 @@ +--- + invalid: +invalid: +--- + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.prettier-snap new file mode 100644 index 000000000000..20048515d179 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.prettier-snap @@ -0,0 +1,9 @@ +--- + invalid: +invalid: +--- + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.snap new file mode 100644 index 000000000000..7f90c1a919d2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/invalid.html.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/yaml/invalid.html +--- +# Input + +```html +--- + invalid: +invalid: +--- + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,7 +1,4 @@ +---- +- invalid: +-invalid: +---- ++--- invalid: invalid: --- + + + +``` + +# Output + +```html +--- invalid: invalid: --- + + + + + +``` diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html new file mode 100644 index 000000000000..2e48c4aa797e --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html @@ -0,0 +1,10 @@ +--- +hello: world +--- + + + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.prettier-snap b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.prettier-snap new file mode 100644 index 000000000000..534de7a0ea1d --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.prettier-snap @@ -0,0 +1,8 @@ +--- +hello: world +--- + + + + + diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.snap new file mode 100644 index 000000000000..09df20ead4d2 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/prettier/html/yaml/yaml.html.snap @@ -0,0 +1,46 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: html/yaml/yaml.html +--- +# Input + +```html +--- +hello: world +--- + + + + + + + + +``` + + +# Prettier differences + +```diff +--- Prettier ++++ Biome +@@ -1,6 +1,4 @@ +---- +-hello: world +---- ++--- hello: world --- + + + +``` + +# Output + +```html +--- hello: world --- + + + + + +``` diff --git a/crates/biome_html_parser/src/lexer/mod.rs b/crates/biome_html_parser/src/lexer/mod.rs index 1fc9dcb9f6d2..b3ea9f279370 100644 --- a/crates/biome_html_parser/src/lexer/mod.rs +++ b/crates/biome_html_parser/src/lexer/mod.rs @@ -60,8 +60,12 @@ impl<'src> HtmlLexer<'src> { b'=' => self.consume_byte(T![=]), b'!' => self.consume_byte(T![!]), b'\'' | b'"' => self.consume_string_literal(current), - // TODO: differentiate between attribute names and identifiers - _ if is_identifier_byte(current) || is_attribute_name_byte(current) => { + _ if self.current_kind == T![<] && is_tag_name_byte(current) => { + // tag names must immediately follow a `<` + // https://html.spec.whatwg.org/multipage/syntax.html#start-tags + self.consume_tag_name(current) + } + _ if self.current_kind != T![<] && is_attribute_name_byte(current) => { self.consume_identifier(current, false) } _ => { @@ -80,7 +84,25 @@ impl<'src> HtmlLexer<'src> { fn consume_token_outside_tag(&mut self, current: u8) -> HtmlSyntaxKind { match current { b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(), - b'<' => self.consume_l_angle(), + b'<' => { + // if this truly is the start of a tag, it *must* be immediately followed by a tag name. Whitespace is not allowed. + // https://html.spec.whatwg.org/multipage/syntax.html#start-tags + if self + .peek_byte() + .is_some_and(|b| is_tag_name_byte(b) || b == b'!' || b == b'/') + { + self.consume_l_angle() + } else { + self.push_diagnostic( + ParseDiagnostic::new( + "Unescaped `<` bracket character. Expected a tag or escaped character.", + self.text_position()..self.text_position() + TextSize::from(1), + ) + .with_hint("Replace this character with `<` to escape it."), + ); + self.consume_byte(HTML_LITERAL) + } + } _ => self.consume_html_text(), } } @@ -104,7 +126,7 @@ impl<'src> HtmlLexer<'src> { b'>' => self.consume_byte(T![>]), b'!' => self.consume_byte(T![!]), b'\'' | b'"' => self.consume_string_literal(current), - _ if is_identifier_byte(current) || is_attribute_name_byte(current) => { + _ if is_tag_name_byte(current) || is_attribute_name_byte(current) => { self.consume_identifier(current, true) } _ => self.consume_unexpected_character(), @@ -119,13 +141,17 @@ impl<'src> HtmlLexer<'src> { ) -> HtmlSyntaxKind { let start = self.text_position(); let end_tag = lang.end_tag(); - while self.current_byte().is_some() { - if self.source[self.position..(self.position + end_tag.len())] - .eq_ignore_ascii_case(end_tag) + self.assert_current_char_boundary(); + while let Some(byte) = self.current_byte() { + let end = self.position + end_tag.len(); + let both_ends_at_char_boundaries = + self.source.is_char_boundary(self.position) && self.source.is_char_boundary(end); + if both_ends_at_char_boundaries + && self.source[self.position..end].eq_ignore_ascii_case(end_tag) { break; } - self.advance(1); + self.advance_byte_or_char(byte); } if self.text_position() != start { @@ -155,6 +181,24 @@ impl<'src> HtmlLexer<'src> { } } + /// Consume a token in the [HtmlLexContext::CdataSection] context. + fn consume_inside_cdata(&mut self, current: u8) -> HtmlSyntaxKind { + match current { + b'<' if self.at_start_cdata() => self.consume_cdata_start(), + b']' if self.at_end_cdata() => self.consume_cdata_end(), + _ => { + while let Some(char) = self.current_byte() { + if self.at_end_cdata() { + // eat ]]> + break; + } + self.advance_byte_or_char(char); + } + HTML_LITERAL + } + } + } + /// Bumps the current byte and creates a lexed token of the passed in kind. #[inline] fn consume_byte(&mut self, tok: HtmlSyntaxKind) -> HtmlSyntaxKind { @@ -193,7 +237,7 @@ impl<'src> HtmlLexer<'src> { self.advance_byte_or_char(first); while let Some(byte) = self.current_byte() { - if is_identifier_byte(byte) || is_attribute_name_byte(byte) { + if is_attribute_name_byte(byte) { if len < BUFFER_SIZE { buffer[len] = byte; len += 1; @@ -212,6 +256,32 @@ impl<'src> HtmlLexer<'src> { } } + fn consume_tag_name(&mut self, first: u8) -> HtmlSyntaxKind { + self.assert_current_char_boundary(); + + const BUFFER_SIZE: usize = 14; + let mut buffer = [0u8; BUFFER_SIZE]; + buffer[0] = first; + let mut len = 1; + + self.advance_byte_or_char(first); + + while let Some(byte) = self.current_byte() { + if is_tag_name_byte(byte) { + if len < BUFFER_SIZE { + buffer[len] = byte; + len += 1; + } + + self.advance(1) + } else { + break; + } + } + + HTML_LITERAL + } + fn consume_string_literal(&mut self, quote: u8) -> HtmlSyntaxKind { self.assert_current_char_boundary(); let start = self.text_position(); @@ -328,6 +398,8 @@ impl<'src> HtmlLexer<'src> { if self.at_start_comment() { self.consume_comment_start() + } else if self.at_start_cdata() { + self.consume_cdata_start() } else { self.consume_byte(T![<]) } @@ -346,6 +418,24 @@ impl<'src> HtmlLexer<'src> { && self.byte_at(2) == Some(b'>') } + fn at_start_cdata(&mut self) -> bool { + self.current_byte() == Some(b'<') + && self.byte_at(1) == Some(b'!') + && self.byte_at(2) == Some(b'[') + && self.byte_at(3) == Some(b'C') + && self.byte_at(4) == Some(b'D') + && self.byte_at(5) == Some(b'A') + && self.byte_at(6) == Some(b'T') + && self.byte_at(7) == Some(b'A') + && self.byte_at(8) == Some(b'[') + } + + fn at_end_cdata(&mut self) -> bool { + self.current_byte() == Some(b']') + && self.byte_at(1) == Some(b']') + && self.byte_at(2) == Some(b'>') + } + fn consume_comment_start(&mut self) -> HtmlSyntaxKind { debug_assert!(self.at_start_comment()); @@ -360,6 +450,20 @@ impl<'src> HtmlLexer<'src> { T![-->] } + fn consume_cdata_start(&mut self) -> HtmlSyntaxKind { + debug_assert!(self.at_start_cdata()); + + self.advance(9); + T![" HtmlSyntaxKind { + debug_assert!(self.at_end_cdata()); + + self.advance(3); + T!["]]>"] + } + /// Lexes a `\u0000` escape sequence. Assumes that the lexer is positioned at the `u` token. /// /// A unicode escape sequence must consist of 4 hex characters. @@ -417,24 +521,32 @@ impl<'src> HtmlLexer<'src> { /// See: https://html.spec.whatwg.org/#space-separated-tokens /// See: https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace fn consume_html_text(&mut self) -> HtmlSyntaxKind { - let mut saw_space = false; + let mut whitespace_started = None; while let Some(current) = self.current_byte() { match current { - b'<' => break, + b'<' => { + if let Some(checkpoint) = whitespace_started { + // avoid treating the last space as part of the token if there is one + self.rewind(checkpoint); + } + break; + } b'\n' | b'\r' => { self.after_newline = true; break; } b' ' => { - if saw_space { + if let Some(checkpoint) = whitespace_started { + // avoid treating the last space as part of the token + self.rewind(checkpoint); break; } + whitespace_started = Some(self.checkpoint()); self.advance(1); - saw_space = true; } _ => { self.advance(1); - saw_space = false; + whitespace_started = None; } } } @@ -479,6 +591,7 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> { self.consume_token_embedded_language(current, lang) } HtmlLexContext::Comment => self.consume_inside_comment(current), + HtmlLexContext::CdataSection => self.consume_inside_cdata(current), }, None => EOF, } @@ -546,8 +659,9 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> { } } -fn is_identifier_byte(byte: u8) -> bool { +fn is_tag_name_byte(byte: u8) -> bool { // https://html.spec.whatwg.org/#elements-2 + // https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-name byte.is_ascii_alphanumeric() } diff --git a/crates/biome_html_parser/src/lexer/tests.rs b/crates/biome_html_parser/src/lexer/tests.rs index 4e78f822a246..dbf8411069f4 100644 --- a/crates/biome_html_parser/src/lexer/tests.rs +++ b/crates/biome_html_parser/src/lexer/tests.rs @@ -263,6 +263,30 @@ fn long_text() { } } +#[test] +fn text_trailing_whitespace() { + assert_lex! { + HtmlLexContext::OutsideTag, + "Lorem ipsum dolor ", + CDATA_START: 9, + HTML_LITERAL: 1, + CDATA_END: 3, + } +} diff --git a/crates/biome_html_parser/src/syntax/mod.rs b/crates/biome_html_parser/src/syntax/mod.rs index 617883c47242..5366251f380a 100644 --- a/crates/biome_html_parser/src/syntax/mod.rs +++ b/crates/biome_html_parser/src/syntax/mod.rs @@ -21,7 +21,7 @@ static VOID_ELEMENTS: &[&str] = &[ ]; /// For these elements, the content is treated as raw text and no parsing is done inside them. This is so that the contents of these tags can be parsed by a different parser. -pub(crate) static EMBEDDED_LANGUAGE_ELEMENTS: &[&str] = &["script", "style"]; +pub(crate) static EMBEDDED_LANGUAGE_ELEMENTS: &[&str] = &["script", "style", "pre"]; pub(crate) fn parse_root(p: &mut HtmlParser) { let m = p.start(); @@ -104,6 +104,7 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { HtmlLexContext::EmbeddedLanguage(match opening_tag_name.as_str() { tag if tag.eq_ignore_ascii_case("script") => HtmlEmbededLanguage::Script, tag if tag.eq_ignore_ascii_case("style") => HtmlEmbededLanguage::Style, + tag if tag.eq_ignore_ascii_case("pre") => HtmlEmbededLanguage::Preformatted, _ => unreachable!(), }) } else { @@ -114,7 +115,7 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { loop { ElementList.parse_list(p); if let Some(mut closing) = - parse_closing_element(p).or_add_diagnostic(p, expected_closing_tag) + parse_closing_tag(p).or_add_diagnostic(p, expected_closing_tag) { if !closing.text(p).contains(opening_tag_name.as_str()) { p.error(expected_matching_closing_tag(p, closing.range(p)).into_diagnostic(p)); @@ -130,7 +131,7 @@ fn parse_element(p: &mut HtmlParser) -> ParsedSyntax { } } -fn parse_closing_element(p: &mut HtmlParser) -> ParsedSyntax { +fn parse_closing_tag(p: &mut HtmlParser) -> ParsedSyntax { if !p.at(T![<]) || !p.nth_at(1, T![/]) { return Absent; } @@ -159,6 +160,7 @@ impl ParseNodeList for ElementList { fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { match p.cur() { T![]) && !p.at(EOF) { + p.bump_with_context(HTML_LITERAL, HtmlLexContext::Comment); + } p.expect(T![-->]); Present(m.complete(p, HTML_COMMENT)) } + +fn parse_cdata_section(p: &mut HtmlParser) -> ParsedSyntax { + if !p.at(T![""]) && !p.at(EOF) { + p.bump_with_context(HTML_LITERAL, HtmlLexContext::CdataSection); + } + p.expect(T!["]]>"]); + Present(m.complete(p, HTML_CDATA_SECTION)) +} diff --git a/crates/biome_html_parser/src/token_source.rs b/crates/biome_html_parser/src/token_source.rs index d5a80699fa50..88a2871f4299 100644 --- a/crates/biome_html_parser/src/token_source.rs +++ b/crates/biome_html_parser/src/token_source.rs @@ -35,12 +35,15 @@ pub(crate) enum HtmlLexContext { EmbeddedLanguage(HtmlEmbededLanguage), /// Comments are treated as text until the closing comment tag is encountered. Comment, + /// CDATA Sections are treated as text until the closing CDATA token is encountered. + CdataSection, } #[derive(Copy, Clone, Debug)] pub(crate) enum HtmlEmbededLanguage { Script, Style, + Preformatted, } impl HtmlEmbededLanguage { @@ -48,6 +51,7 @@ impl HtmlEmbededLanguage { match self { Self::Script => "", Self::Style => "", + Self::Preformatted => "", } } } @@ -104,7 +108,7 @@ impl<'source> HtmlTokenSource<'source> { } } -impl<'src> TokenSource for HtmlTokenSource<'src> { +impl TokenSource for HtmlTokenSource<'_> { type Kind = HtmlSyntaxKind; fn current(&self) -> Self::Kind { @@ -136,7 +140,7 @@ impl<'src> TokenSource for HtmlTokenSource<'src> { } } -impl<'source> BumpWithContext for HtmlTokenSource<'source> { +impl BumpWithContext for HtmlTokenSource<'_> { type Context = HtmlLexContext; fn bump_with_context(&mut self, context: Self::Context) { if self.current() != EOF { diff --git a/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html b/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html new file mode 100644 index 000000000000..192ef94a264d --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html @@ -0,0 +1 @@ +
    <
    diff --git a/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html.snap b/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html.snap new file mode 100644 index 000000000000..5b919bdcd96e --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/child-no-tag-name.html.snap @@ -0,0 +1,88 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html +
    <
    + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlElement { + opening_element: HtmlOpeningElement { + l_angle_token: L_ANGLE@0..1 "<" [] [], + name: HtmlName { + value_token: HTML_LITERAL@1..4 "div" [] [], + }, + attributes: HtmlAttributeList [], + r_angle_token: R_ANGLE@4..5 ">" [] [], + }, + children: HtmlElementList [ + HtmlContent { + value_token: HTML_LITERAL@5..6 "<" [] [], + }, + ], + closing_element: HtmlClosingElement { + l_angle_token: L_ANGLE@6..7 "<" [] [], + slash_token: SLASH@7..8 "/" [] [], + name: HtmlName { + value_token: HTML_LITERAL@8..11 "div" [] [], + }, + r_angle_token: R_ANGLE@11..12 ">" [] [], + }, + }, + ], + eof_token: EOF@12..13 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..13 + 0: (empty) + 1: (empty) + 2: HTML_ELEMENT_LIST@0..12 + 0: HTML_ELEMENT@0..12 + 0: HTML_OPENING_ELEMENT@0..5 + 0: L_ANGLE@0..1 "<" [] [] + 1: HTML_NAME@1..4 + 0: HTML_LITERAL@1..4 "div" [] [] + 2: HTML_ATTRIBUTE_LIST@4..4 + 3: R_ANGLE@4..5 ">" [] [] + 1: HTML_ELEMENT_LIST@5..6 + 0: HTML_CONTENT@5..6 + 0: HTML_LITERAL@5..6 "<" [] [] + 2: HTML_CLOSING_ELEMENT@6..12 + 0: L_ANGLE@6..7 "<" [] [] + 1: SLASH@7..8 "/" [] [] + 2: HTML_NAME@8..11 + 0: HTML_LITERAL@8..11 "div" [] [] + 3: R_ANGLE@11..12 ">" [] [] + 3: EOF@12..13 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +child-no-tag-name.html:1:6 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unescaped `<` bracket character. Expected a tag or escaped character. + + > 1 │
    <
    + │ ^ + 2 │ + + i Replace this character with `<` to escape it. + +``` diff --git a/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html b/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html new file mode 100644 index 000000000000..e54a27324331 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html @@ -0,0 +1 @@ +
    diff --git a/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html.snap b/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html.snap new file mode 100644 index 000000000000..1eecf30e88bd --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/missing-close-tag-2.html.snap @@ -0,0 +1,74 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html +
    + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlElement { + opening_element: HtmlOpeningElement { + l_angle_token: L_ANGLE@0..1 "<" [] [], + name: HtmlName { + value_token: HTML_LITERAL@1..4 "div" [] [], + }, + attributes: HtmlAttributeList [], + r_angle_token: R_ANGLE@4..5 ">" [] [], + }, + children: HtmlElementList [], + closing_element: missing (required), + }, + ], + eof_token: EOF@5..6 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..6 + 0: (empty) + 1: (empty) + 2: HTML_ELEMENT_LIST@0..5 + 0: HTML_ELEMENT@0..5 + 0: HTML_OPENING_ELEMENT@0..5 + 0: L_ANGLE@0..1 "<" [] [] + 1: HTML_NAME@1..4 + 0: HTML_LITERAL@1..4 "div" [] [] + 2: HTML_ATTRIBUTE_LIST@4..4 + 3: R_ANGLE@4..5 ">" [] [] + 1: HTML_ELEMENT_LIST@5..5 + 2: (empty) + 3: EOF@5..6 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +missing-close-tag-2.html:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a closing tag but instead found the end of the file. + + 1 │
    + > 2 │ + │ + + i Expected a closing tag here. + + 1 │
    + > 2 │ + │ + +``` diff --git a/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html new file mode 100644 index 000000000000..9318418344a8 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html @@ -0,0 +1 @@ +< diff --git a/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap new file mode 100644 index 000000000000..bec8b50fb679 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/error/element/solo-no-tag-name.html.snap @@ -0,0 +1,71 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html +< + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlElement { + opening_element: HtmlOpeningElement { + l_angle_token: L_ANGLE@0..1 "<" [] [], + name: missing (required), + attributes: HtmlAttributeList [], + r_angle_token: missing (required), + }, + children: HtmlElementList [], + closing_element: missing (required), + }, + ], + eof_token: EOF@1..2 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..2 + 0: (empty) + 1: (empty) + 2: HTML_ELEMENT_LIST@0..1 + 0: HTML_ELEMENT@0..1 + 0: HTML_OPENING_ELEMENT@0..1 + 0: L_ANGLE@0..1 "<" [] [] + 1: (empty) + 2: HTML_ATTRIBUTE_LIST@1..1 + 3: (empty) + 1: HTML_ELEMENT_LIST@1..1 + 2: (empty) + 3: EOF@1..2 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +solo-no-tag-name.html:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an element name but instead found the end of the file. + + 1 │ < + > 2 │ + │ + + i Expected an element name here. + + 1 │ < + > 2 │ + │ + +``` diff --git a/crates/biome_html_parser/tests/html_specs/ok/cdata.html b/crates/biome_html_parser/tests/html_specs/ok/cdata.html new file mode 100644 index 000000000000..3ba13a82273d --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/cdata.html @@ -0,0 +1,4 @@ + + + +
    ]]> diff --git a/crates/biome_html_parser/tests/html_specs/ok/cdata.html.snap b/crates/biome_html_parser/tests/html_specs/ok/cdata.html.snap new file mode 100644 index 000000000000..82e3a4871a67 --- /dev/null +++ b/crates/biome_html_parser/tests/html_specs/ok/cdata.html.snap @@ -0,0 +1,73 @@ +--- +source: crates/biome_html_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```html + + + +
    ]]> + +``` + + +## AST + +``` +HtmlRoot { + bom_token: missing (optional), + directive: missing (optional), + html: HtmlElementList [ + HtmlCdataSection { + cdata_start_token: CDATA_START@0..9 "" [] [], + }, + HtmlCdataSection { + cdata_start_token: CDATA_START@19..29 "" [] [], + }, + HtmlCdataSection { + cdata_start_token: CDATA_START@35..45 "" [] [], + }, + HtmlCdataSection { + cdata_start_token: CDATA_START@151..161 "
    " [] [], + cdata_end_token: CDATA_END@200..203 "]]>" [] [], + }, + ], + eof_token: EOF@203..204 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: HTML_ROOT@0..204 + 0: (empty) + 1: (empty) + 2: HTML_ELEMENT_LIST@0..203 + 0: HTML_CDATA_SECTION@0..19 + 0: CDATA_START@0..9 "" [] [] + 1: HTML_CDATA_SECTION@19..35 + 0: CDATA_START@19..29 "" [] [] + 2: HTML_CDATA_SECTION@35..151 + 0: CDATA_START@35..45 "" [] [] + 3: HTML_CDATA_SECTION@151..203 + 0: CDATA_START@151..161 "
    " [] [] + 2: CDATA_END@200..203 "]]>" [] [] + 3: EOF@203..204 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_html_syntax/Cargo.toml b/crates/biome_html_syntax/Cargo.toml index 29cdaa13eb47..d618cb0e4e2e 100644 --- a/crates/biome_html_syntax/Cargo.toml +++ b/crates/biome_html_syntax/Cargo.toml @@ -15,9 +15,9 @@ version = "0.5.7" [dependencies] biome_rowan = { workspace = true, features = ["serde"] } biome_string_case = { workspace = true } +camino = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } - [features] schema = ["schemars", "biome_rowan/serde"] diff --git a/crates/biome_html_syntax/src/file_source.rs b/crates/biome_html_syntax/src/file_source.rs index f39a730ce222..15ad33f584ca 100644 --- a/crates/biome_html_syntax/src/file_source.rs +++ b/crates/biome_html_syntax/src/file_source.rs @@ -1,6 +1,6 @@ use biome_rowan::FileSourceError; use biome_string_case::StrLikeExtension; -use std::{ffi::OsStr, path::Path}; +use camino::Utf8Path; #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive( @@ -33,17 +33,17 @@ impl HtmlFileSource { } /// Try to return the HTML file source corresponding to this file name from well-known files - pub fn try_from_well_known(_: &Path) -> Result { + pub fn try_from_well_known(_: &Utf8Path) -> Result { // TODO: to be implemented Err(FileSourceError::UnknownFileName) } /// Try to return the HTML file source corresponding to this file extension - pub fn try_from_extension(extension: &OsStr) -> Result { + pub fn try_from_extension(extension: &str) -> Result { // We assume the file extension is normalized to lowercase - match extension.as_encoded_bytes() { - b"html" => Ok(Self::html()), - b"astro" => Ok(Self::astro()), + match extension { + "html" => Ok(Self::html()), + "astro" => Ok(Self::astro()), _ => Err(FileSourceError::UnknownExtension), } } @@ -66,10 +66,10 @@ impl HtmlFileSource { } } -impl TryFrom<&Path> for HtmlFileSource { +impl TryFrom<&Utf8Path> for HtmlFileSource { type Error = FileSourceError; - fn try_from(path: &Path) -> Result { + fn try_from(path: &Utf8Path) -> Result { if let Ok(file_source) = Self::try_from_well_known(path) { return Ok(file_source); } diff --git a/crates/biome_html_syntax/src/generated/kind.rs b/crates/biome_html_syntax/src/generated/kind.rs index 3dae9444dccd..496a5c0d8908 100644 --- a/crates/biome_html_syntax/src/generated/kind.rs +++ b/crates/biome_html_syntax/src/generated/kind.rs @@ -20,6 +20,8 @@ pub enum HtmlSyntaxKind { MINUS, COMMENT_START, COMMENT_END, + CDATA_START, + CDATA_END, NULL_KW, TRUE_KW, FALSE_KW, @@ -47,6 +49,7 @@ pub enum HtmlSyntaxKind { HTML_ATTRIBUTE_LIST, HTML_CONTENT, HTML_COMMENT, + HTML_CDATA_SECTION, HTML_BOGUS, HTML_BOGUS_ELEMENT, HTML_BOGUS_ATTRIBUTE, @@ -58,7 +61,16 @@ impl HtmlSyntaxKind { pub const fn is_punct(self) -> bool { matches!( self, - L_ANGLE | R_ANGLE | SLASH | EQ | BANG | MINUS | COMMENT_START | COMMENT_END + L_ANGLE + | R_ANGLE + | SLASH + | EQ + | BANG + | MINUS + | COMMENT_START + | COMMENT_END + | CDATA_START + | CDATA_END ) } pub const fn is_literal(self) -> bool { @@ -88,6 +100,8 @@ impl HtmlSyntaxKind { MINUS => "-", COMMENT_START => "", + CDATA_START => " "]]>", NULL_KW => "null", TRUE_KW => "true", FALSE_KW => "false", @@ -101,4 +115,4 @@ impl HtmlSyntaxKind { } #[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] #[macro_export] -macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [] => { $ crate :: HtmlSyntaxKind :: COMMENT_END } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; } +macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [] => { $ crate :: HtmlSyntaxKind :: COMMENT_END } ; [" { $ crate :: HtmlSyntaxKind :: CDATA_START } ; ["]]>"] => { $ crate :: HtmlSyntaxKind :: CDATA_END } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; } diff --git a/crates/biome_html_syntax/src/generated/macros.rs b/crates/biome_html_syntax/src/generated/macros.rs index 20801883a1a6..f392e67772e6 100644 --- a/crates/biome_html_syntax/src/generated/macros.rs +++ b/crates/biome_html_syntax/src/generated/macros.rs @@ -25,6 +25,10 @@ macro_rules! map_syntax_node { unsafe { $crate::HtmlAttributeInitializerClause::new_unchecked(node) }; $body } + $crate::HtmlSyntaxKind::HTML_CDATA_SECTION => { + let $pattern = unsafe { $crate::HtmlCdataSection::new_unchecked(node) }; + $body + } $crate::HtmlSyntaxKind::HTML_CLOSING_ELEMENT => { let $pattern = unsafe { $crate::HtmlClosingElement::new_unchecked(node) }; $body diff --git a/crates/biome_html_syntax/src/generated/nodes.rs b/crates/biome_html_syntax/src/generated/nodes.rs index 0d8e14a5e5ac..b394db8b0259 100644 --- a/crates/biome_html_syntax/src/generated/nodes.rs +++ b/crates/biome_html_syntax/src/generated/nodes.rs @@ -100,6 +100,51 @@ pub struct HtmlAttributeInitializerClauseFields { pub value: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct HtmlCdataSection { + pub(crate) syntax: SyntaxNode, +} +impl HtmlCdataSection { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> HtmlCdataSectionFields { + HtmlCdataSectionFields { + cdata_start_token: self.cdata_start_token(), + content_token: self.content_token(), + cdata_end_token: self.cdata_end_token(), + } + } + pub fn cdata_start_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn content_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn cdata_end_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for HtmlCdataSection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct HtmlCdataSectionFields { + pub cdata_start_token: SyntaxResult, + pub content_token: SyntaxResult, + pub cdata_end_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct HtmlClosingElement { pub(crate) syntax: SyntaxNode, } @@ -591,6 +636,7 @@ impl AnyHtmlAttribute { #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyHtmlElement { HtmlBogusElement(HtmlBogusElement), + HtmlCdataSection(HtmlCdataSection), HtmlComment(HtmlComment), HtmlContent(HtmlContent), HtmlElement(HtmlElement), @@ -603,6 +649,12 @@ impl AnyHtmlElement { _ => None, } } + pub fn as_html_cdata_section(&self) -> Option<&HtmlCdataSection> { + match &self { + AnyHtmlElement::HtmlCdataSection(item) => Some(item), + _ => None, + } + } pub fn as_html_comment(&self) -> Option<&HtmlComment> { match &self { AnyHtmlElement::HtmlComment(item) => Some(item), @@ -651,13 +703,22 @@ impl AstNode for HtmlAttribute { } impl std::fmt::Debug for HtmlAttribute { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlAttribute") - .field("name", &support::DebugSyntaxResult(self.name())) - .field( - "initializer", - &support::DebugOptionalElement(self.initializer()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlAttribute") + .field("name", &support::DebugSyntaxResult(self.name())) + .field( + "initializer", + &support::DebugOptionalElement(self.initializer()), + ) + .finish() + } else { + f.debug_struct("HtmlAttribute").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -693,10 +754,19 @@ impl AstNode for HtmlAttributeInitializerClause { } impl std::fmt::Debug for HtmlAttributeInitializerClause { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlAttributeInitializerClause") - .field("eq_token", &support::DebugSyntaxResult(self.eq_token())) - .field("value", &support::DebugSyntaxResult(self.value())) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlAttributeInitializerClause") + .field("eq_token", &support::DebugSyntaxResult(self.eq_token())) + .field("value", &support::DebugSyntaxResult(self.value())) + .finish() + } else { + f.debug_struct("HtmlAttributeInitializerClause").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -709,6 +779,64 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for HtmlCdataSection { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(HTML_CDATA_SECTION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == HTML_CDATA_SECTION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for HtmlCdataSection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlCdataSection") + .field( + "cdata_start_token", + &support::DebugSyntaxResult(self.cdata_start_token()), + ) + .field( + "content_token", + &support::DebugSyntaxResult(self.content_token()), + ) + .field( + "cdata_end_token", + &support::DebugSyntaxResult(self.cdata_end_token()), + ) + .finish() + } else { + f.debug_struct("HtmlCdataSection").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: HtmlCdataSection) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: HtmlCdataSection) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for HtmlClosingElement { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -732,21 +860,30 @@ impl AstNode for HtmlClosingElement { } impl std::fmt::Debug for HtmlClosingElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlClosingElement") - .field( - "l_angle_token", - &support::DebugSyntaxResult(self.l_angle_token()), - ) - .field( - "slash_token", - &support::DebugSyntaxResult(self.slash_token()), - ) - .field("name", &support::DebugSyntaxResult(self.name())) - .field( - "r_angle_token", - &support::DebugSyntaxResult(self.r_angle_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlClosingElement") + .field( + "l_angle_token", + &support::DebugSyntaxResult(self.l_angle_token()), + ) + .field( + "slash_token", + &support::DebugSyntaxResult(self.slash_token()), + ) + .field("name", &support::DebugSyntaxResult(self.name())) + .field( + "r_angle_token", + &support::DebugSyntaxResult(self.r_angle_token()), + ) + .finish() + } else { + f.debug_struct("HtmlClosingElement").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -782,20 +919,29 @@ impl AstNode for HtmlComment { } impl std::fmt::Debug for HtmlComment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlComment") - .field( - "comment_start_token", - &support::DebugSyntaxResult(self.comment_start_token()), - ) - .field( - "content_token", - &support::DebugSyntaxResult(self.content_token()), - ) - .field( - "comment_end_token", - &support::DebugSyntaxResult(self.comment_end_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlComment") + .field( + "comment_start_token", + &support::DebugSyntaxResult(self.comment_start_token()), + ) + .field( + "content_token", + &support::DebugSyntaxResult(self.content_token()), + ) + .field( + "comment_end_token", + &support::DebugSyntaxResult(self.comment_end_token()), + ) + .finish() + } else { + f.debug_struct("HtmlComment").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -831,12 +977,21 @@ impl AstNode for HtmlContent { } impl std::fmt::Debug for HtmlContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlContent") - .field( - "value_token", - &support::DebugSyntaxResult(self.value_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlContent") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } else { + f.debug_struct("HtmlContent").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -872,37 +1027,46 @@ impl AstNode for HtmlDirective { } impl std::fmt::Debug for HtmlDirective { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlDirective") - .field( - "l_angle_token", - &support::DebugSyntaxResult(self.l_angle_token()), - ) - .field("excl_token", &support::DebugSyntaxResult(self.excl_token())) - .field( - "doctype_token", - &support::DebugSyntaxResult(self.doctype_token()), - ) - .field( - "html_token", - &support::DebugOptionalElement(self.html_token()), - ) - .field( - "quirk_token", - &support::DebugOptionalElement(self.quirk_token()), - ) - .field( - "public_id_token", - &support::DebugOptionalElement(self.public_id_token()), - ) - .field( - "system_id_token", - &support::DebugOptionalElement(self.system_id_token()), - ) - .field( - "r_angle_token", - &support::DebugSyntaxResult(self.r_angle_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlDirective") + .field( + "l_angle_token", + &support::DebugSyntaxResult(self.l_angle_token()), + ) + .field("excl_token", &support::DebugSyntaxResult(self.excl_token())) + .field( + "doctype_token", + &support::DebugSyntaxResult(self.doctype_token()), + ) + .field( + "html_token", + &support::DebugOptionalElement(self.html_token()), + ) + .field( + "quirk_token", + &support::DebugOptionalElement(self.quirk_token()), + ) + .field( + "public_id_token", + &support::DebugOptionalElement(self.public_id_token()), + ) + .field( + "system_id_token", + &support::DebugOptionalElement(self.system_id_token()), + ) + .field( + "r_angle_token", + &support::DebugSyntaxResult(self.r_angle_token()), + ) + .finish() + } else { + f.debug_struct("HtmlDirective").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -938,17 +1102,26 @@ impl AstNode for HtmlElement { } impl std::fmt::Debug for HtmlElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlElement") - .field( - "opening_element", - &support::DebugSyntaxResult(self.opening_element()), - ) - .field("children", &self.children()) - .field( - "closing_element", - &support::DebugSyntaxResult(self.closing_element()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlElement") + .field( + "opening_element", + &support::DebugSyntaxResult(self.opening_element()), + ) + .field("children", &self.children()) + .field( + "closing_element", + &support::DebugSyntaxResult(self.closing_element()), + ) + .finish() + } else { + f.debug_struct("HtmlElement").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -984,12 +1157,21 @@ impl AstNode for HtmlName { } impl std::fmt::Debug for HtmlName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlName") - .field( - "value_token", - &support::DebugSyntaxResult(self.value_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlName") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } else { + f.debug_struct("HtmlName").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -1025,18 +1207,27 @@ impl AstNode for HtmlOpeningElement { } impl std::fmt::Debug for HtmlOpeningElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlOpeningElement") - .field( - "l_angle_token", - &support::DebugSyntaxResult(self.l_angle_token()), - ) - .field("name", &support::DebugSyntaxResult(self.name())) - .field("attributes", &self.attributes()) - .field( - "r_angle_token", - &support::DebugSyntaxResult(self.r_angle_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlOpeningElement") + .field( + "l_angle_token", + &support::DebugSyntaxResult(self.l_angle_token()), + ) + .field("name", &support::DebugSyntaxResult(self.name())) + .field("attributes", &self.attributes()) + .field( + "r_angle_token", + &support::DebugSyntaxResult(self.r_angle_token()), + ) + .finish() + } else { + f.debug_struct("HtmlOpeningElement").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -1072,18 +1263,27 @@ impl AstNode for HtmlRoot { } impl std::fmt::Debug for HtmlRoot { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlRoot") - .field( - "bom_token", - &support::DebugOptionalElement(self.bom_token()), - ) - .field( - "directive", - &support::DebugOptionalElement(self.directive()), - ) - .field("html", &self.html()) - .field("eof_token", &support::DebugSyntaxResult(self.eof_token())) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlRoot") + .field( + "bom_token", + &support::DebugOptionalElement(self.bom_token()), + ) + .field( + "directive", + &support::DebugOptionalElement(self.directive()), + ) + .field("html", &self.html()) + .field("eof_token", &support::DebugSyntaxResult(self.eof_token())) + .finish() + } else { + f.debug_struct("HtmlRoot").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -1119,22 +1319,31 @@ impl AstNode for HtmlSelfClosingElement { } impl std::fmt::Debug for HtmlSelfClosingElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlSelfClosingElement") - .field( - "l_angle_token", - &support::DebugSyntaxResult(self.l_angle_token()), - ) - .field("name", &support::DebugSyntaxResult(self.name())) - .field("attributes", &self.attributes()) - .field( - "slash_token", - &support::DebugOptionalElement(self.slash_token()), - ) - .field( - "r_angle_token", - &support::DebugSyntaxResult(self.r_angle_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlSelfClosingElement") + .field( + "l_angle_token", + &support::DebugSyntaxResult(self.l_angle_token()), + ) + .field("name", &support::DebugSyntaxResult(self.name())) + .field("attributes", &self.attributes()) + .field( + "slash_token", + &support::DebugOptionalElement(self.slash_token()), + ) + .field( + "r_angle_token", + &support::DebugSyntaxResult(self.r_angle_token()), + ) + .finish() + } else { + f.debug_struct("HtmlSelfClosingElement").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -1170,12 +1379,21 @@ impl AstNode for HtmlString { } impl std::fmt::Debug for HtmlString { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("HtmlString") - .field( - "value_token", - &support::DebugSyntaxResult(self.value_token()), - ) - .finish() + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("HtmlString") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } else { + f.debug_struct("HtmlString").finish() + }; + DEPTH.set(current_depth); + result } } impl From for SyntaxNode { @@ -1255,6 +1473,11 @@ impl From for AnyHtmlElement { AnyHtmlElement::HtmlBogusElement(node) } } +impl From for AnyHtmlElement { + fn from(node: HtmlCdataSection) -> AnyHtmlElement { + AnyHtmlElement::HtmlCdataSection(node) + } +} impl From for AnyHtmlElement { fn from(node: HtmlComment) -> AnyHtmlElement { AnyHtmlElement::HtmlComment(node) @@ -1278,6 +1501,7 @@ impl From for AnyHtmlElement { impl AstNode for AnyHtmlElement { type Language = Language; const KIND_SET: SyntaxKindSet = HtmlBogusElement::KIND_SET + .union(HtmlCdataSection::KIND_SET) .union(HtmlComment::KIND_SET) .union(HtmlContent::KIND_SET) .union(HtmlElement::KIND_SET) @@ -1286,6 +1510,7 @@ impl AstNode for AnyHtmlElement { matches!( kind, HTML_BOGUS_ELEMENT + | HTML_CDATA_SECTION | HTML_COMMENT | HTML_CONTENT | HTML_ELEMENT @@ -1295,6 +1520,7 @@ impl AstNode for AnyHtmlElement { fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { HTML_BOGUS_ELEMENT => AnyHtmlElement::HtmlBogusElement(HtmlBogusElement { syntax }), + HTML_CDATA_SECTION => AnyHtmlElement::HtmlCdataSection(HtmlCdataSection { syntax }), HTML_COMMENT => AnyHtmlElement::HtmlComment(HtmlComment { syntax }), HTML_CONTENT => AnyHtmlElement::HtmlContent(HtmlContent { syntax }), HTML_ELEMENT => AnyHtmlElement::HtmlElement(HtmlElement { syntax }), @@ -1308,6 +1534,7 @@ impl AstNode for AnyHtmlElement { fn syntax(&self) -> &SyntaxNode { match self { AnyHtmlElement::HtmlBogusElement(it) => &it.syntax, + AnyHtmlElement::HtmlCdataSection(it) => &it.syntax, AnyHtmlElement::HtmlComment(it) => &it.syntax, AnyHtmlElement::HtmlContent(it) => &it.syntax, AnyHtmlElement::HtmlElement(it) => &it.syntax, @@ -1317,6 +1544,7 @@ impl AstNode for AnyHtmlElement { fn into_syntax(self) -> SyntaxNode { match self { AnyHtmlElement::HtmlBogusElement(it) => it.syntax, + AnyHtmlElement::HtmlCdataSection(it) => it.syntax, AnyHtmlElement::HtmlComment(it) => it.syntax, AnyHtmlElement::HtmlContent(it) => it.syntax, AnyHtmlElement::HtmlElement(it) => it.syntax, @@ -1328,6 +1556,7 @@ impl std::fmt::Debug for AnyHtmlElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AnyHtmlElement::HtmlBogusElement(it) => std::fmt::Debug::fmt(it, f), + AnyHtmlElement::HtmlCdataSection(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlComment(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlContent(it) => std::fmt::Debug::fmt(it, f), AnyHtmlElement::HtmlElement(it) => std::fmt::Debug::fmt(it, f), @@ -1339,6 +1568,7 @@ impl From for SyntaxNode { fn from(n: AnyHtmlElement) -> SyntaxNode { match n { AnyHtmlElement::HtmlBogusElement(it) => it.into(), + AnyHtmlElement::HtmlCdataSection(it) => it.into(), AnyHtmlElement::HtmlComment(it) => it.into(), AnyHtmlElement::HtmlContent(it) => it.into(), AnyHtmlElement::HtmlElement(it) => it.into(), @@ -1372,6 +1602,11 @@ impl std::fmt::Display for HtmlAttributeInitializerClause { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for HtmlCdataSection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for HtmlClosingElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -1590,6 +1825,7 @@ impl From for SyntaxElement { n.syntax.into() } } +biome_rowan::declare_node_union! { pub AnyHtmlBogusNode = HtmlBogus | HtmlBogusAttribute | HtmlBogusElement } #[derive(Clone, Eq, PartialEq, Hash)] pub struct HtmlAttributeList { syntax_list: SyntaxList, diff --git a/crates/biome_html_syntax/src/generated/nodes_mut.rs b/crates/biome_html_syntax/src/generated/nodes_mut.rs index fb793fd99740..5b6db033d550 100644 --- a/crates/biome_html_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_html_syntax/src/generated/nodes_mut.rs @@ -31,6 +31,26 @@ impl HtmlAttributeInitializerClause { ) } } +impl HtmlCdataSection { + pub fn with_cdata_start_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_content_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_cdata_end_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} impl HtmlClosingElement { pub fn with_l_angle_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/crates/biome_js_analyze/Cargo.toml b/crates/biome_js_analyze/Cargo.toml index 4ca099d57254..91917112ada5 100644 --- a/crates/biome_js_analyze/Cargo.toml +++ b/crates/biome_js_analyze/Cargo.toml @@ -16,19 +16,22 @@ biome_aria = { workspace = true } biome_aria_metadata = { workspace = true } biome_console = { workspace = true } biome_control_flow = { workspace = true } +biome_dependency_graph = { workspace = true } biome_deserialize = { workspace = true, features = ["smallvec"] } biome_deserialize_macros = { workspace = true } biome_diagnostics = { workspace = true } -biome_glob = { workspace = true, features = ["biome_deserialize", "schemars", "serde"] } +biome_glob = { workspace = true, features = ["biome_deserialize", "serde"] } biome_js_factory = { workspace = true } biome_js_semantic = { workspace = true } biome_js_syntax = { workspace = true } -biome_project = { workspace = true } +biome_package = { workspace = true } +biome_project_layout = { workspace = true } biome_rowan = { workspace = true } biome_string_case = { workspace = true } biome_suppression = { workspace = true } biome_unicode_table = { workspace = true } bitvec = "1.0.1" +camino = { workspace = true } enumflags2 = { workspace = true } globset = { workspace = true } natord = { workspace = true } @@ -40,13 +43,15 @@ serde = { workspace = true, features = ["derive"] } smallvec = { workspace = true } [dev-dependencies] -biome_js_parser = { path = "../biome_js_parser", features = ["tests"] } -biome_test_utils = { path = "../biome_test_utils" } -insta = { workspace = true, features = ["glob"] } -tests_macros = { path = "../tests_macros" } +biome_fs = { workspace = true } +biome_js_parser = { path = "../biome_js_parser", features = ["tests"] } +biome_plugin_loader = { workspace = true } +biome_test_utils = { path = "../biome_test_utils" } +insta = { workspace = true, features = ["glob"] } +tests_macros = { path = "../tests_macros" } [features] -schema = ["schemars", "biome_deserialize/schema"] +schema = ["schemars", "biome_glob/schema"] [lints] workspace = true diff --git a/crates/biome_js_analyze/src/assist.rs b/crates/biome_js_analyze/src/assist.rs new file mode 100644 index 000000000000..6174b359db67 --- /dev/null +++ b/crates/biome_js_analyze/src/assist.rs @@ -0,0 +1,6 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +//! Generated file, do not edit by hand, see `xtask/codegen` + +pub mod source; +::biome_analyze::declare_category! { pub Assist { kind : Action , groups : [self :: source :: Source ,] } } diff --git a/crates/biome_js_analyze/src/assist/source.rs b/crates/biome_js_analyze/src/assist/source.rs new file mode 100644 index 000000000000..ca871e3b5164 --- /dev/null +++ b/crates/biome_js_analyze/src/assist/source.rs @@ -0,0 +1,8 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +//! Generated file, do not edit by hand, see `xtask/codegen` + +use biome_analyze::declare_assist_group; +pub mod organize_imports; +pub mod use_sorted_attributes; +declare_assist_group! { pub Source { name : "source" , rules : [self :: organize_imports :: OrganizeImports , self :: use_sorted_attributes :: UseSortedAttributes ,] } } diff --git a/crates/biome_js_analyze/src/assists/source/organize_imports.rs b/crates/biome_js_analyze/src/assist/source/organize_imports.rs similarity index 92% rename from crates/biome_js_analyze/src/assists/source/organize_imports.rs rename to crates/biome_js_analyze/src/assist/source/organize_imports.rs index 56b83d78bc95..8ea0ab3502c2 100644 --- a/crates/biome_js_analyze/src/assists/source/organize_imports.rs +++ b/crates/biome_js_analyze/src/assist/source/organize_imports.rs @@ -2,7 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_source_rule, ActionCategory, Ast, FixKind, Rule, SourceActionKind, }; use biome_console::markup; -use biome_deserialize::Deserializable; +use biome_deserialize::{Deserializable, DeserializableValue, DeserializationContext}; use biome_deserialize_macros::Deserializable; use biome_js_syntax::JsModule; use biome_rowan::BatchMutationExt; @@ -40,8 +40,8 @@ declare_source_rule! { version: "1.0.0", name: "organizeImports", language: "js", - recommended: false, - fix_kind: FixKind::Unsafe, + recommended: true, + fix_kind: FixKind::Safe, } } @@ -98,15 +98,15 @@ pub enum ImportGroup { } impl Deserializable for ImportGroup { fn deserialize( - value: &impl biome_deserialize::DeserializableValue, + ctx: &mut impl DeserializationContext, + value: &impl DeserializableValue, name: &str, - diagnostics: &mut Vec, ) -> Option { Some( - if let Some(predefined) = Deserializable::deserialize(value, name, diagnostics) { + if let Some(predefined) = Deserializable::deserialize(ctx, value, name) { ImportGroup::Predefined(predefined) } else { - ImportGroup::Custom(Deserializable::deserialize(value, name, diagnostics)?) + ImportGroup::Custom(Deserializable::deserialize(ctx, value, name)?) }, ) } diff --git a/crates/biome_js_analyze/src/assists/source/organize_imports/legacy.rs b/crates/biome_js_analyze/src/assist/source/organize_imports/legacy.rs similarity index 99% rename from crates/biome_js_analyze/src/assists/source/organize_imports/legacy.rs rename to crates/biome_js_analyze/src/assist/source/organize_imports/legacy.rs index 3f5fd7df92c3..713e03be96df 100644 --- a/crates/biome_js_analyze/src/assists/source/organize_imports/legacy.rs +++ b/crates/biome_js_analyze/src/assist/source/organize_imports/legacy.rs @@ -1,5 +1,4 @@ -use std::{cell::Cell, cmp::Ordering, collections::BTreeMap, iter}; - +use biome_analyze::QueryMatch; use biome_js_factory::make; use biome_js_syntax::{ AnyJsImportClause, AnyJsModuleItem, AnyJsNamedImportSpecifier, JsImport, JsLanguage, JsModule, @@ -9,6 +8,7 @@ use biome_rowan::{ chain_trivia_pieces, AstNode, AstNodeExt, AstNodeList, AstSeparatedList, BatchMutation, SyntaxTriviaPiece, TextRange, TokenText, TriviaPiece, TriviaPieceKind, }; +use std::{cell::Cell, cmp::Ordering, collections::BTreeMap, iter}; pub(crate) fn run(root: &JsModule) -> Option { let mut groups = Vec::new(); @@ -392,7 +392,7 @@ impl ImportNode { let will_need_newline = sep .trailing_trivia() .last() - .map_or(false, |piece| piece.kind().is_single_line_comment()); + .is_some_and(|piece| piece.kind().is_single_line_comment()); (sep.clone(), will_need_newline) } else { @@ -474,7 +474,7 @@ fn prepend_leading_newline( let leading_trivia = prev_token.leading_trivia(); let has_leading_newline = leading_trivia .first() - .map_or(false, |piece| piece.is_newline()); + .is_some_and(|piece| piece.is_newline()); if has_leading_newline { return None; diff --git a/crates/biome_js_analyze/src/assists/source/organize_imports/util.rs b/crates/biome_js_analyze/src/assist/source/organize_imports/util.rs similarity index 98% rename from crates/biome_js_analyze/src/assists/source/organize_imports/util.rs rename to crates/biome_js_analyze/src/assist/source/organize_imports/util.rs index 223ea4718796..0ef8a575234b 100644 --- a/crates/biome_js_analyze/src/assists/source/organize_imports/util.rs +++ b/crates/biome_js_analyze/src/assist/source/organize_imports/util.rs @@ -15,7 +15,7 @@ use biome_string_case::AsciiCollator; /// See [ImportSourceAsciiCollator] for more details. /// /// ``` -/// use biome_js_analyze::assists::source::organize_imports::util::ImportSource; +/// use biome_js_analyze::assist::source::organize_imports::util::ImportSource; /// /// assert!(ImportSource::from("https://example.org") < ImportSource::from("bun:test")); /// assert!(ImportSource::from("node:test") < ImportSource::from("@scope/package")); @@ -161,12 +161,12 @@ enum PathComponent<'a> { /// See [Component::Normal] Normal(&'a std::ffi::OsStr), } -impl<'a> PartialOrd for PathComponent<'a> { +impl PartialOrd for PathComponent<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl<'a> Ord for PathComponent<'a> { +impl Ord for PathComponent<'_> { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (PathComponent::RootDir, PathComponent::RootDir) diff --git a/crates/biome_js_analyze/src/assists/source/use_sorted_attributes.rs b/crates/biome_js_analyze/src/assist/source/use_sorted_attributes.rs similarity index 94% rename from crates/biome_js_analyze/src/assists/source/use_sorted_attributes.rs rename to crates/biome_js_analyze/src/assist/source/use_sorted_attributes.rs index 32e86e8713bf..2f611ac00a3d 100644 --- a/crates/biome_js_analyze/src/assists/source/use_sorted_attributes.rs +++ b/crates/biome_js_analyze/src/assist/source/use_sorted_attributes.rs @@ -1,8 +1,7 @@ use std::{borrow::Cow, cmp::Ordering, iter::zip}; use biome_analyze::{ - context::RuleContext, declare_source_rule, ActionCategory, Ast, Rule, RuleAction, RuleSource, - RuleSourceKind, SourceActionKind, + context::RuleContext, declare_source_rule, Ast, Rule, RuleAction, RuleSource, RuleSourceKind, }; use biome_console::markup; use biome_diagnostics::Applicability; @@ -100,7 +99,10 @@ impl Ord for PropElement { let (Ok(self_name), Ok(other_name)) = (self.prop.name(), other.prop.name()) else { return Ordering::Equal; }; - let (a_name, b_name) = (self_name.text(), other_name.text()); + let (a_name, b_name) = ( + self_name.to_trimmed_string(), + other_name.to_trimmed_string(), + ); a_name.cmp(&b_name) } diff --git a/crates/biome_js_analyze/src/assists/source.rs b/crates/biome_js_analyze/src/assists/source.rs deleted file mode 100644 index c40c54220c62..000000000000 --- a/crates/biome_js_analyze/src/assists/source.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Generated file, do not edit by hand, see `xtask/codegen` - -//! Generated file, do not edit by hand, see `xtask/codegen` - -use biome_analyze::declare_assists_group; -pub mod organize_imports; -pub mod use_sorted_attributes; -declare_assists_group! { pub Source { name : "source" , rules : [self :: organize_imports :: OrganizeImports , self :: use_sorted_attributes :: UseSortedAttributes ,] } } diff --git a/crates/biome_js_analyze/src/ast_utils.rs b/crates/biome_js_analyze/src/ast_utils.rs index 8932b9a46ebb..74b6264a7be5 100644 --- a/crates/biome_js_analyze/src/ast_utils.rs +++ b/crates/biome_js_analyze/src/ast_utils.rs @@ -1,5 +1,10 @@ -use biome_js_syntax::{JsLanguage, JsSyntaxNode, JsSyntaxToken}; -use biome_rowan::{AstNode, TriviaPiece}; +use biome_js_semantic::{BindingExtensions, SemanticModel}; +use biome_js_syntax::{ + AnyJsArrayElement, AnyJsExpression, AnyJsLiteralExpression, AnyJsTemplateElement, + JsAssignmentOperator, JsLanguage, JsLogicalOperator, JsSyntaxNode, JsSyntaxToken, + JsUnaryOperator, +}; +use biome_rowan::{AstNode, AstSeparatedList, TriviaPiece}; /// Add any leading and trailing trivia from given source node to the token. /// @@ -59,3 +64,300 @@ fn add_trailing_trivia(trivia: &mut Vec, text: &mut String, node: & trivia.push(TriviaPiece::whitespace(1)); } } + +pub fn is_constant_condition( + test: AnyJsExpression, + in_boolean_position: bool, + model: &SemanticModel, +) -> Option<()> { + use AnyJsExpression::*; + + match test.omit_parentheses() { + AnyJsLiteralExpression(_) + | JsObjectExpression(_) + | JsFunctionExpression(_) + | JsArrowFunctionExpression(_) + | JsClassExpression(_) => Some(()), + JsUnaryExpression(node) => { + use JsUnaryOperator::*; + + let op = node.operator().ok()?; + if op == Void || op == Typeof && in_boolean_position { + return Some(()); + } + if op == LogicalNot { + return is_constant_condition(node.argument().ok()?, true, model); + } + is_constant_condition(node.argument().ok()?, false, model) + } + JsBinaryExpression(node) => is_constant_condition(node.left().ok()?, false, model) + .and_then(|_| is_constant_condition(node.right().ok()?, false, model)), + JsLogicalExpression(node) => { + let left = node.left().ok()?; + let right = node.right().ok()?; + let op = node.operator().ok()?; + let is_left_constant = + is_constant_condition(left.clone(), in_boolean_position, model).is_some(); + let is_right_constant = + is_constant_condition(right.clone(), in_boolean_position, model).is_some(); + + let is_left_short_circuit = is_left_constant && is_logical_identity(left, op); + let is_right_short_circuit = + in_boolean_position && is_right_constant && is_logical_identity(right, op); + + if (is_left_constant && is_right_constant) + || is_left_short_circuit + || is_right_short_circuit + { + Some(()) + } else { + None + } + } + JsSequenceExpression(node) => { + is_constant_condition(node.right().ok()?, in_boolean_position, model) + } + JsIdentifierExpression(node) => { + if node.name().ok()?.binding(model).is_some() { + // This is any_js_stmt edge case. Modern browsers don't allow to redeclare `undefined` but ESLint handle this so we do + return None; + } + let is_named_undefined = node.name().ok()?.is_undefined(); + is_named_undefined.then_some(()) + } + JsArrayExpression(node) => { + if !in_boolean_position { + node.elements() + .into_iter() + .all(|js_statement| { + if let Ok(element) = js_statement { + match element { + AnyJsArrayElement::JsArrayHole(_) => true, + AnyJsArrayElement::JsSpread(node) => { + if let Ok(argument) = node.argument() { + is_constant_condition(argument, in_boolean_position, model) + .is_some() + } else { + false + } + } + _ => element + .as_any_js_expression() + .and_then(|node| { + is_constant_condition(node.clone(), false, model) + }) + .is_some(), + } + } else { + false + } + }) + .then_some(()) + } else { + Some(()) + } + } + JsNewExpression(_) => in_boolean_position.then_some(()), + JsCallExpression(node) => { + if node.has_callee("Boolean") { + let callee = node.callee().ok()?; + let ident = callee.as_js_identifier_expression()?.name().ok()?; + let binding = ident.binding(model); + if binding.is_some() { + return None; + } + + let args = node.arguments().ok()?.args(); + if args.is_empty() { + return Some(()); + } + return is_constant_condition( + args.first()?.ok()?.as_any_js_expression()?.clone(), + true, + model, + ); + } + + None + } + JsAssignmentExpression(node) => { + use JsAssignmentOperator::*; + + let operator = node.operator().ok()?; + if operator == Assign { + return is_constant_condition(node.right().ok()?, in_boolean_position, model); + } + + if matches!(operator, LogicalOrAssign | LogicalAndAssign) && in_boolean_position { + let new_op = match operator { + LogicalAndAssign => JsLogicalOperator::LogicalAnd, + LogicalOrAssign => JsLogicalOperator::LogicalOr, + _ => unreachable!(), + }; + + return is_logical_identity(node.right().ok()?, new_op).then_some(()); + } + None + } + JsTemplateExpression(node) => { + let is_tag = node.tag().is_some(); + let elements = node.elements(); + let has_truthy_quasi = !is_tag + && elements.clone().into_iter().any(|element| match element { + AnyJsTemplateElement::JsTemplateChunkElement(element) => { + if let Ok(quasi) = element.template_chunk_token() { + !quasi.text_trimmed().is_empty() + } else { + false + } + } + AnyJsTemplateElement::JsTemplateElement(_) => false, + }); + if has_truthy_quasi && in_boolean_position { + return Some(()); + } + + elements + .into_iter() + .all(|element| match element { + AnyJsTemplateElement::JsTemplateChunkElement(_) => !is_tag, + AnyJsTemplateElement::JsTemplateElement(element) => { + if let Ok(expr) = element.expression() { + is_constant_condition(expr, false, model).is_some() + } else { + false + } + } + }) + .then_some(()) + } + _ => None, + } +} + +fn is_logical_identity(node: AnyJsExpression, operator: JsLogicalOperator) -> bool { + use AnyJsExpression::*; + use JsLogicalOperator::*; + match node.omit_parentheses() { + AnyJsLiteralExpression(node) => { + let boolean_value = get_boolean_value(&node); + operator == LogicalOr && boolean_value || (operator == LogicalAnd && !boolean_value) + } + JsUnaryExpression(node) => { + if operator != LogicalAnd { + return false; + } + + if let Ok(node_operator) = node.operator() { + node_operator == JsUnaryOperator::Void + } else { + false + } + } + JsLogicalExpression(node) => { + if let Ok(node_operator) = node.operator() { + // handles `any_js_stmt && false || b` + // `false` is an identity element of `&&` but not `||` + // so the logical identity of the whole expression can not be defined. + if operator != node_operator { + return false; + } + + let is_left_logical_identify = node + .left() + .ok() + .is_some_and(|left| is_logical_identity(left, operator)); + if is_left_logical_identify { + return true; + } + + node.right() + .ok() + .is_some_and(|right| is_logical_identity(right, operator)) + } else { + false + } + } + JsAssignmentExpression(node) => { + if let Ok(node_operator) = node.operator() { + if let Ok(right) = node.right() { + let is_valid_logical_assignment = match node_operator { + JsAssignmentOperator::LogicalAndAssign + if operator == JsLogicalOperator::LogicalAnd => + { + true + } + JsAssignmentOperator::LogicalOrAssign + if operator == JsLogicalOperator::LogicalOr => + { + true + } + _ => false, + }; + + is_valid_logical_assignment && is_logical_identity(right, operator) + } else { + false + } + } else { + false + } + } + _ => false, + } +} + +fn get_boolean_value(node: &AnyJsLiteralExpression) -> bool { + use AnyJsLiteralExpression::*; + match node { + JsRegexLiteralExpression(_) => true, + _ => node + .as_static_value() + .is_some_and(|value| !value.is_falsy()), + } +} + +#[cfg(test)] +mod tests { + use biome_js_parser::JsParserOptions; + use biome_js_syntax::{AnyJsLiteralExpression, JsFileSource}; + use biome_rowan::SyntaxNodeCast; + + use super::get_boolean_value; + + fn assert_boolean_value(code: &str, value: bool) { + let source = biome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); + + if source.has_errors() { + panic!("syntax error") + } + + let literal_expression = source + .syntax() + .descendants() + .find_map(|js_statement| js_statement.cast::()); + + assert_eq!( + get_boolean_value(&literal_expression.expect("Not found AnyLiteralExpression.")), + value + ); + } + #[test] + fn test_get_boolean_value() { + assert_boolean_value("false", false); + assert_boolean_value("0", false); + assert_boolean_value("-0", false); + assert_boolean_value("0n", false); + assert_boolean_value("let any_js_stmt =\"\"", false); + assert_boolean_value("let any_js_stmt = ''", false); + assert_boolean_value("null", false); + + assert_boolean_value("true", true); + assert_boolean_value("let any_js_stmt = \"0\"", true); + assert_boolean_value("let any_js_stmt = \"false\"", true); + assert_boolean_value("-42", true); + assert_boolean_value("12n", true); + assert_boolean_value("3.14", true); + assert_boolean_value("-3.14", true); + } +} diff --git a/crates/biome_js_analyze/src/lib.rs b/crates/biome_js_analyze/src/lib.rs index 66e6512a0884..799dfafb790e 100644 --- a/crates/biome_js_analyze/src/lib.rs +++ b/crates/biome_js_analyze/src/lib.rs @@ -2,22 +2,25 @@ use crate::suppression_action::JsSuppressionAction; use biome_analyze::{ - AnalysisFilter, Analyzer, AnalyzerContext, AnalyzerOptions, AnalyzerSignal, ControlFlow, - InspectMatcher, LanguageRoot, MatchQueryParams, MetadataRegistry, RuleAction, RuleRegistry, - SuppressionKind, + to_analyzer_suppressions, AnalysisFilter, Analyzer, AnalyzerContext, AnalyzerOptions, + AnalyzerPlugin, AnalyzerSignal, AnalyzerSuppression, ControlFlow, InspectMatcher, LanguageRoot, + MatchQueryParams, MetadataRegistry, RuleAction, RuleRegistry, }; use biome_aria::AriaRoles; -use biome_diagnostics::{category, Error as DiagnosticError}; +use biome_dependency_graph::DependencyGraph; +use biome_diagnostics::Error as DiagnosticError; use biome_js_syntax::{JsFileSource, JsLanguage}; -use biome_project::PackageJson; +use biome_project_layout::ProjectLayout; +use biome_rowan::TextRange; use biome_suppression::{parse_suppression_comment, SuppressionDiagnostic}; use std::ops::Deref; use std::sync::{Arc, LazyLock}; -pub mod assists; +pub mod assist; mod ast_utils; pub mod globals; pub mod lint; +mod nextjs; pub mod options; mod react; mod registry; @@ -37,6 +40,29 @@ pub static METADATA: LazyLock = LazyLock::new(|| { metadata }); +#[derive(Default)] +pub struct JsAnalyzerServices { + dependency_graph: Arc, + project_layout: Arc, + source_type: JsFileSource, +} + +impl From<(Arc, Arc, JsFileSource)> for JsAnalyzerServices { + fn from( + (dependency_graph, project_layout, source_type): ( + Arc, + Arc, + JsFileSource, + ), + ) -> Self { + Self { + dependency_graph, + project_layout, + source_type, + } + } +} + /// Run the analyzer on the provided `root`: this process will use the given `filter` /// to selectively restrict analysis to specific rules / a specific source range, /// then call `emit_signal` when an analysis rule emits a diagnostic or action. @@ -48,8 +74,8 @@ pub fn analyze_with_inspect_matcher<'a, V, F, B>( filter: AnalysisFilter, inspect_matcher: V, options: &'a AnalyzerOptions, - source_type: JsFileSource, - manifest: Option, + plugins: Vec>, + services: JsAnalyzerServices, mut emit_signal: F, ) -> (Option, Vec) where @@ -59,41 +85,25 @@ where { fn parse_linter_suppression_comment( text: &str, - ) -> Vec> { + piece_range: TextRange, + ) -> Vec> { let mut result = Vec::new(); for comment in parse_suppression_comment(text) { - let categories = match comment { - Ok(comment) => { - if comment.is_legacy { - result.push(Ok(SuppressionKind::Deprecated)); - } - comment.categories - } + let suppression = match comment { + Ok(suppression) => suppression, Err(err) => { result.push(Err(err)); continue; } }; - for (key, value) in categories { - if key == category!("lint") { - if let Some(value) = value { - result.push(Ok(SuppressionKind::MaybeLegacy(value))); - } else { - result.push(Ok(SuppressionKind::Everything)); - } - } else { - let category = key.name(); - if let Some(rule) = category.strip_prefix("lint/") { - if let Some(instance) = value { - result.push(Ok(SuppressionKind::RuleInstance(rule, instance))); - } else { - result.push(Ok(SuppressionKind::Rule(rule))); - } - } - } - } + let analyzer_suppressions: Vec<_> = to_analyzer_suppressions(suppression, piece_range) + .into_iter() + .map(Ok) + .collect(); + + result.extend(analyzer_suppressions) } result @@ -102,6 +112,12 @@ where let mut registry = RuleRegistry::builder(&filter, root); visit_registry(&mut registry); + let JsAnalyzerServices { + dependency_graph, + project_layout, + source_type, + } = services; + let (registry, mut services, diagnostics, visitors) = registry.build(); // Bail if we can't parse a rule option @@ -117,13 +133,22 @@ where &mut emit_signal, ); + for plugin in plugins { + if plugin.supports_js() { + analyzer.add_plugin(plugin); + } + } + for ((phase, _), visitor) in visitors { analyzer.add_visitor(phase, visitor); } services.insert_service(Arc::new(AriaRoles)); - services.insert_service(Arc::new(manifest)); services.insert_service(source_type); + services.insert_service(dependency_graph); + services.insert_service(project_layout.get_node_manifest_for_path(&options.file_path)); + services.insert_service(project_layout); + ( analyzer.run(AnalyzerContext { root: root.clone(), @@ -142,8 +167,8 @@ pub fn analyze<'a, F, B>( root: &LanguageRoot, filter: AnalysisFilter, options: &'a AnalyzerOptions, - source_type: JsFileSource, - manifest: Option, + plugins: Vec>, + services: JsAnalyzerServices, emit_signal: F, ) -> (Option, Vec) where @@ -155,8 +180,8 @@ where filter, |_| {}, options, - source_type, - manifest, + plugins, + services, emit_signal, ) } @@ -164,40 +189,44 @@ where #[cfg(test)] mod tests { use biome_analyze::{AnalyzerOptions, Never, RuleCategoriesBuilder, RuleFilter}; - use biome_console::fmt::{Formatter, Termcolor}; - use biome_console::{markup, Markup}; use biome_diagnostics::category; - use biome_diagnostics::termcolor::NoColor; - use biome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic, Severity}; + use biome_diagnostics::{print_diagnostic_to_string, Diagnostic, DiagnosticExt, Severity}; use biome_js_parser::{parse, JsParserOptions}; use biome_js_syntax::{JsFileSource, TextRange, TextSize}; - use biome_project::{Dependencies, PackageJson}; + use biome_package::{Dependencies, PackageJson}; use std::slice; - use crate::{analyze, AnalysisFilter, ControlFlow}; + use super::*; - #[ignore] + // #[ignore] #[test] fn quick_test() { - fn markup_to_string(markup: Markup) -> String { - let mut buffer = Vec::new(); - let mut write = Termcolor(NoColor::new(&mut buffer)); - let mut fmt = Formatter::new(&mut write); - fmt.write_markup(markup).unwrap(); + const SOURCE: &str = r#" - String::from_utf8(buffer).unwrap() - } + /** +* biome-ignore lint/style/useConst: reason + */ - const SOURCE: &str = r#"import buffer from "buffer"; "#; + +let foo = 2; +let bar = 33; + "#; let parsed = parse(SOURCE, JsFileSource::tsx(), JsParserOptions::default()); let mut error_ranges: Vec = Vec::new(); let options = AnalyzerOptions::default(); - let rule_filter = RuleFilter::Rule("style", "useNodejsImportProtocol"); + let rule_filter = RuleFilter::Rule("style", "useConst"); let mut dependencies = Dependencies::default(); dependencies.add("buffer", "latest"); + + let services = JsAnalyzerServices::from(( + Default::default(), + project_layout_with_top_level_dependencies(dependencies), + JsFileSource::tsx(), + )); + analyze( &parsed.tree(), AnalysisFilter { @@ -205,11 +234,8 @@ mod tests { ..AnalysisFilter::default() }, &options, - JsFileSource::tsx(), - Some(PackageJson { - dependencies, - ..Default::default() - }), + Vec::new(), + services, |signal| { if let Some(diag) = signal.diagnostic() { error_ranges.push(diag.location().span.unwrap()); @@ -217,9 +243,7 @@ mod tests { .with_severity(Severity::Warning) .with_file_path("dummyFile") .with_file_source_code(SOURCE); - let text = markup_to_string(markup! { - {PrintDiagnostic::verbose(&error)} - }); + let text = print_diagnostic_to_string(&error); eprintln!("{text}"); } @@ -236,21 +260,65 @@ mod tests { // assert_eq!(error_ranges.as_slice(), &[]); } + #[test] + fn quick_test_suppression() { + const SOURCE: &str = " + function checkSuppressions1(a, b) { + // biome-ignore lint/suspicious:whole group + p == f; + // biome-ignore lint/suspicious/noDoubleEquals: single rule + j == k; + } + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + AnalysisFilter::default(), + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_severity(Severity::Warning) + .with_file_path("example.js") + .with_file_source_code(SOURCE); + + let code = error.category().unwrap(); + if code == category!("lint/suspicious/noDoubleEquals") { + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("unexpected diagnostic"); + } + } + + ControlFlow::::Continue(()) + }, + ); + } + #[test] fn suppression() { const SOURCE: &str = " function checkSuppressions1(a, b) { a == b; // biome-ignore lint/suspicious:whole group - a == b; + p == f; // biome-ignore lint/suspicious/noDoubleEquals: single rule - a == b; - /* biome-ignore lint/style/useWhile: multiple block comments */ /* biome-ignore lint/suspicious/noDoubleEquals: multiple block comments */ - a == b; - // biome-ignore lint/style/useWhile: multiple line comments + j == k; + /* biome-ignore lint/complexity/useWhile: multiple block comments */ /* biome-ignore lint/suspicious/noDoubleEquals: multiple block comments */ + o == m; + // biome-ignore lint/complexity/useWhile: multiple line comments // biome-ignore lint/suspicious/noDoubleEquals: multiple line comments - a == b; - a == b; + d == x; + z == v; } // biome-ignore lint/suspicious/noDoubleEquals: do not suppress warning for the whole function @@ -260,14 +328,14 @@ mod tests { function checkSuppressions3(a, b) { a == b; - // rome-ignore lint/suspicious: whole group + // biome-ignore lint/suspicious: whole group a == b; - // rome-ignore lint/suspicious/noDoubleEquals: single rule + // biome-ignore lint/suspicious/noDoubleEquals: single rule a == b; - /* rome-ignore lint/style/useWhile: multiple block comments */ /* rome-ignore lint(suspicious/noDoubleEquals): multiple block comments */ + /* biome-ignore lint/complexity/useWhile: multiple block comments */ /* biome-ignore lint(suspicious/noDoubleEquals): multiple block comments */ a == b; - // rome-ignore lint/style/useWhile: multiple line comments - // rome-ignore lint/suspicious/noDoubleEquals: multiple line comments + // biome-ignore lint/complexity/useWhile: multiple line comments + // biome-ignore lint/suspicious/noDoubleEquals: multiple line comments a == b; a == b; } @@ -293,15 +361,14 @@ mod tests { let mut lint_ranges: Vec = Vec::new(); let mut parse_ranges: Vec = Vec::new(); - let mut warn_ranges: Vec = Vec::new(); let options = AnalyzerOptions::default(); analyze( &parsed.tree(), AnalysisFilter::default(), &options, - JsFileSource::js_module(), - None, + Vec::new(), + Default::default(), |signal| { if let Some(diag) = signal.diagnostic() { let span = diag.get_span(); @@ -312,16 +379,14 @@ mod tests { let code = error.category().unwrap(); if code == category!("lint/suspicious/noDoubleEquals") { + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); lint_ranges.push(span.unwrap()); } if code == category!("suppressions/parse") { parse_ranges.push(span.unwrap()); } - - if code == category!("suppressions/deprecatedSuppressionComment") { - warn_ranges.push(span.unwrap()); - } } ControlFlow::::Continue(()) @@ -331,34 +396,21 @@ mod tests { lint_ranges.as_slice(), &[ TextRange::new(TextSize::from(67), TextSize::from(69)), - TextRange::new(TextSize::from(641), TextSize::from(643)), - TextRange::new(TextSize::from(835), TextSize::from(837)), - TextRange::new(TextSize::from(922), TextSize::from(924)), - TextRange::new(TextSize::from(1492), TextSize::from(1494)), - TextRange::new(TextSize::from(1687), TextSize::from(1689)), + TextRange::new(TextSize::from(651), TextSize::from(653)), + TextRange::new(TextSize::from(845), TextSize::from(847)), + TextRange::new(TextSize::from(932), TextSize::from(934)), + TextRange::new(TextSize::from(1518), TextSize::from(1520)), + TextRange::new(TextSize::from(1713), TextSize::from(1715)), ] ); assert_eq!( parse_ranges.as_slice(), &[ - TextRange::new(TextSize::from(1791), TextSize::from(1802)), - TextRange::new(TextSize::from(1842), TextSize::from(1843)), - TextRange::new(TextSize::from(1876), TextSize::from(1877)), - TextRange::new(TextSize::from(1929), TextSize::from(1936)), - ] - ); - - assert_eq!( - warn_ranges.as_slice(), - &[ - TextRange::new(TextSize::from(944), TextSize::from(987)), - TextRange::new(TextSize::from(1028), TextSize::from(1086)), - TextRange::new(TextSize::from(1127), TextSize::from(1189)), - TextRange::new(TextSize::from(1190), TextSize::from(1264)), - TextRange::new(TextSize::from(1305), TextSize::from(1363)), - TextRange::new(TextSize::from(1380), TextSize::from(1449)), - TextRange::new(TextSize::from(1525), TextSize::from(1620)), + TextRange::new(TextSize::from(1817), TextSize::from(1828)), + TextRange::new(TextSize::from(1868), TextSize::from(1869)), + TextRange::new(TextSize::from(1902), TextSize::from(1903)), + TextRange::new(TextSize::from(1955), TextSize::from(1962)), ] ); } @@ -386,10 +438,536 @@ mod tests { &parsed.tree(), filter, &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let code = diag.category().unwrap(); + if code != category!("suppressions/unused") { + panic!("unexpected diagnostic {code:?}"); + } + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_simple() { + const SOURCE: &str = " +/** +* biome-ignore-all lint/style/useConst: reason +*/ + + +let foo = 2; +let bar = 33; + "; + + let parsed = parse( + SOURCE, JsFileSource::js_module(), - None, + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_multiple() { + const SOURCE: &str = " +/** +* biome-ignore-all lint/style/useConst: reason +*/ + +/** +* biome-ignore-all lint/suspicious/noDebugger: reason2 +*/ + + +let foo = 2; +let bar = 33; +debugger; + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), |signal| { if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_all() { + const SOURCE: &str = " +/** +* biome-ignore-all lint: reason +*/ + +let foo = 2; +let bar = 33; +debugger; + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_multiple2() { + const SOURCE: &str = " +/** +* biome-ignore-all lint/style/useConst: reason +* biome-ignore-all lint/suspicious/noDebugger: reason2 +*/ + + +let foo = 2; +let bar = 33; +debugger; + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_with_unused() { + const SOURCE: &str = " +/** +* biome-ignore-all lint/style/useConst: reason +*/ + + +let foo = 2; +/** +* biome-ignore lint/style/useConst: reason +*/ +let bar = 33; + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let code = diag.category().unwrap(); + if code != category!("suppressions/unused") { + panic!("unexpected diagnostic {code:?}"); + } + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn top_level_suppression_with_block_comment() { + const SOURCE: &str = " +/* +* Top level comment here. It could be a banner or a license comment +* MIT +*/ +/** +* biome-ignore-all lint/style/useConst: reason +*/ + +let foo = 2; +let bar = 33; + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("style", "useConst")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn suppression_range_should_report_after_end() { + const SOURCE: &str = " +// biome-ignore-start lint/suspicious/noDoubleEquals: single rule +// biome-ignore-start lint/style/useConst: single rule +a == b; +let c; +// biome-ignore-end lint/suspicious/noDoubleEquals: single rule +a == b; +let c; + + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[ + RuleFilter::Rule("suspicious", "noDoubleEquals"), + RuleFilter::Rule("style", "useConst"), + ]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let code = diag.category().unwrap(); + if code != category!("lint/suspicious/noDoubleEquals") { + panic!("unexpected diagnostic {code:?}"); + } + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn suppression_range_should_report_after_end_v2() { + const SOURCE: &str = " +// biome-ignore-start lint/suspicious/noDoubleEquals: single rule +// biome-ignore-start lint/suspicious/noDebugger: single rule +a == b; +debugger; +// biome-ignore-end lint/suspicious/noDoubleEquals: single rule +a === b; +debugger; +// biome-ignore-end lint/suspicious/noDebugger: single rule +debugger; + + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[ + RuleFilter::Rule("suspicious", "noDoubleEquals"), + RuleFilter::Rule("suspicious", "noDebugger"), + ]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + let mut has_diagnostics = false; + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + has_diagnostics = true; + let code = diag.category().unwrap(); + if code != category!("lint/suspicious/noDebugger") { + panic!("unexpected diagnostic {code:?}"); + } + } + + ControlFlow::::Continue(()) + }, + ); + assert!(has_diagnostics, "must have diagnostics"); + } + + #[test] + fn suppression_range_should_not_report_after_end() { + const SOURCE: &str = " +// biome-ignore-start lint/suspicious/noDoubleEquals: single rule +// biome-ignore-start lint/style/useConst: single rule +a == b; +let c; +// biome-ignore-end lint/suspicious/noDoubleEquals: single rule +a === b; +let f; +// biome-ignore-end lint/style/useConst: single rule +let d; + + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[ + RuleFilter::Rule("suspicious", "noDoubleEquals"), + RuleFilter::Rule("style", "useConst"), + ]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }, + ); + } + + #[test] + fn suppression_range_should_report_when_contains_inner_comment() { + const SOURCE: &str = " +// biome-ignore lint/complexity/useArrowFunction: single rule +const foo0 = function (bar: string) { + // biome-ignore lint/style/noParameterAssign: single rule + bar = 'baz'; +};"; + + let parsed = parse(SOURCE, JsFileSource::ts(), JsParserOptions::default()); + + let enabled_rules = vec![ + RuleFilter::Rule("complexity", "useArrowFunction"), + RuleFilter::Rule("style", "noParameterAssign"), + ]; + + let filter = AnalysisFilter { + enabled_rules: Some(enabled_rules.as_slice()), + ..AnalysisFilter::default() + }; + let options = AnalyzerOptions::default(); + let root = parsed.tree(); + + let services = + JsAnalyzerServices::from((Default::default(), Default::default(), JsFileSource::ts())); + + analyze(&root, filter, &options, Vec::new(), services, |signal| { + if let Some(diag) = signal.diagnostic() { + let error = diag + .with_file_path("dummyFile") + .with_file_source_code(SOURCE); + let text = print_diagnostic_to_string(&error); + eprintln!("{text}"); + panic!("Unexpected diagnostic"); + } + + ControlFlow::::Continue(()) + }); + } + + #[test] + fn unused_range_suppression() { + const SOURCE: &str = " +// biome-ignore-all lint/suspicious/noDoubleEquals: single rule +a == b; +// biome-ignore-start lint/suspicious/noDoubleEquals: single rule +a == b; +a == b; +// biome-ignore-end lint/suspicious/noDoubleEquals: single rule + + "; + + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); + + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default().with_lint().build(), + enabled_rules: Some(&[RuleFilter::Rule("suspicious", "noDoubleEquals")]), + ..AnalysisFilter::default() + }; + + let options = AnalyzerOptions::default(); + let mut has_diagnostics = false; + analyze( + &parsed.tree(), + filter, + &options, + Vec::new(), + Default::default(), + |signal| { + if let Some(diag) = signal.diagnostic() { + has_diagnostics = true; let code = diag.category().unwrap(); if code != category!("suppressions/unused") { panic!("unexpected diagnostic {code:?}"); @@ -399,5 +977,17 @@ mod tests { ControlFlow::::Continue(()) }, ); + assert!(has_diagnostics, "must have diagnostics"); + } + + fn project_layout_with_top_level_dependencies( + dependencies: Dependencies, + ) -> Arc { + let manifest = PackageJson::default().with_dependencies(dependencies); + + let project_layout = ProjectLayout::default(); + project_layout.insert_node_manifest("/".into(), manifest); + + Arc::new(project_layout) } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_access_key.rs b/crates/biome_js_analyze/src/lint/a11y/no_access_key.rs index 8aaf6e6690c0..5cfea0775106 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_access_key.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_access_key.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, JsxAttribute, JsxAttributeList}; use biome_rowan::{AstNode, BatchMutationExt}; @@ -41,6 +42,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-access-key")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_aria_hidden_on_focusable.rs b/crates/biome_js_analyze/src/lint/a11y/no_aria_hidden_on_focusable.rs index d6fed513ee66..0a72c7b6fbe9 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_aria_hidden_on_focusable.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_aria_hidden_on_focusable.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsxAttributeValue, AnyNumberLikeExpression}; use biome_rowan::{AstNode, BatchMutationExt}; @@ -51,6 +52,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-aria-hidden-on-focusable")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -77,7 +79,7 @@ impl Rule for NoAriaHiddenOnFocusable { match tabindex_val { AnyJsxAttributeValue::AnyJsxTag(jsx_tag) => { - let value = jsx_tag.text().parse::(); + let value = jsx_tag.to_trimmed_string().parse::(); if let Ok(num) = value { return (num >= 0).then_some(()); } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_aria_unsupported_elements.rs b/crates/biome_js_analyze/src/lint/a11y/no_aria_unsupported_elements.rs index 392d0b481e07..436084b752c6 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_aria_unsupported_elements.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_aria_unsupported_elements.rs @@ -6,6 +6,7 @@ use biome_analyze::{ }; use biome_aria_metadata::AriaAttribute; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, AstNodeList, BatchMutationExt}; @@ -41,6 +42,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("aria-unsupported-elements")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_autofocus.rs b/crates/biome_js_analyze/src/lint/a11y/no_autofocus.rs index 4f35b238af8b..717e62293fc6 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_autofocus.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_autofocus.rs @@ -4,6 +4,7 @@ use biome_analyze::{ RuleDiagnostic, RuleSource, Visitor, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, JsLanguage, JsSyntaxKind, JsxAttribute}; use biome_rowan::{AstNode, BatchMutationExt, WalkEvent}; use biome_string_case::StrOnlyExtension; @@ -76,6 +77,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-autofocus")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_blank_target.rs b/crates/biome_js_analyze/src/lint/a11y/no_blank_target.rs index 4898dbd12d23..558dcc95c100 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_blank_target.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_blank_target.rs @@ -3,6 +3,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; use biome_deserialize_macros::Deserializable; +use biome_diagnostics::Severity; use biome_js_factory::make::{ jsx_attribute, jsx_attribute_initializer_clause, jsx_attribute_list, jsx_ident, jsx_name, jsx_string, jsx_string_literal, token, @@ -93,6 +94,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReact("jsx-no-target-blank")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_distracting_elements.rs b/crates/biome_js_analyze/src/lint/a11y/no_distracting_elements.rs index 6da1ef45f240..78954e74fb69 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_distracting_elements.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_distracting_elements.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::*; use biome_rowan::{AstNode, BatchMutationExt}; @@ -42,6 +43,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-distracting-elements")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_header_scope.rs b/crates/biome_js_analyze/src/lint/a11y/no_header_scope.rs index e8c3989df3ae..b0605dd148ae 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_header_scope.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_header_scope.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, BatchMutationExt}; @@ -42,6 +43,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("scope")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs b/crates/biome_js_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs index 373ef485576b..865103a74646 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs @@ -4,6 +4,7 @@ use biome_analyze::{ }; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, BatchMutationExt}; @@ -42,6 +43,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-interactive-element-to-noninteractive-role")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_label_without_control.rs b/crates/biome_js_analyze/src/lint/a11y/no_label_without_control.rs index bbe686756897..28675943b573 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_label_without_control.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_label_without_control.rs @@ -3,6 +3,7 @@ use biome_analyze::{ }; use biome_console::markup; use biome_deserialize_macros::Deserializable; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsxAttribute, AnyJsxAttributeName, AnyJsxAttributeValue, AnyJsxElementName, AnyJsxTag, JsSyntaxKind, JsxAttribute, @@ -89,6 +90,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("label-has-associated-control")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_element_to_interactive_role.rs b/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_element_to_interactive_role.rs index cbc3ac182ab5..fe4fe17d36ae 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_element_to_interactive_role.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_element_to_interactive_role.rs @@ -4,6 +4,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, BatchMutationExt, TextRange, TokenText}; @@ -51,6 +52,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-noninteractive-element-to-interactive-role")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_tabindex.rs b/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_tabindex.rs index c73cc83faa3d..31fd0c7333dd 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_tabindex.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_noninteractive_tabindex.rs @@ -4,6 +4,7 @@ use biome_analyze::{ }; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ jsx_ext::AnyJsxElement, AnyJsxAttributeValue, AnyNumberLikeExpression, TextRange, }; @@ -52,6 +53,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-noninteractive-tabindex")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_positive_tabindex.rs b/crates/biome_js_analyze/src/lint/a11y/no_positive_tabindex.rs index bed4117bb456..61c1b0b973b9 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_positive_tabindex.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_positive_tabindex.rs @@ -4,6 +4,7 @@ use crate::JsRuleAction; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_factory::make::{jsx_string, jsx_string_literal}; use biome_js_semantic::SemanticModel; @@ -53,6 +54,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("tabindex-no-positive")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -108,7 +110,7 @@ impl AnyNumberLikeExpression { } AnyNumberLikeExpression::JsUnaryExpression(unary_expression) => { if unary_expression.is_signed_numeric_literal().ok()? { - return Some(unary_expression.text()); + return Some(unary_expression.to_trimmed_string()); } } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_redundant_alt.rs b/crates/biome_js_analyze/src/lint/a11y/no_redundant_alt.rs index 6e45b02e9724..58afa972ac07 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_redundant_alt.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_redundant_alt.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, AnyJsTemplateElement, AnyJsxAttributeValue, @@ -46,6 +47,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("img-redundant-alt")], recommended: true, + severity: Severity::Error, } } @@ -103,7 +105,7 @@ impl Rule for NoRedundantAlt { expr.elements().into_iter().any(|template_element| { match template_element { AnyJsTemplateElement::JsTemplateChunkElement(node) => { - node.template_chunk_token().ok().map_or(false, |token| { + node.template_chunk_token().ok().is_some_and(|token| { is_redundant_alt(token.text_trimmed()) }) } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_redundant_roles.rs b/crates/biome_js_analyze/src/lint/a11y/no_redundant_roles.rs index 792994272078..6596d09e2115 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_redundant_roles.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_redundant_roles.rs @@ -4,6 +4,7 @@ use biome_analyze::{ }; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsxAttributeValue, JsxAttribute}; use biome_rowan::{AstNode, BatchMutationExt}; @@ -46,6 +47,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("no-redundant-roles")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/no_svg_without_title.rs b/crates/biome_js_analyze/src/lint/a11y/no_svg_without_title.rs index f6bf6818b619..75f4f9c122bf 100644 --- a/crates/biome_js_analyze/src/lint/a11y/no_svg_without_title.rs +++ b/crates/biome_js_analyze/src/lint/a11y/no_svg_without_title.rs @@ -1,5 +1,6 @@ use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, JsxAttribute, JsxChildList, JsxElement}; use biome_rowan::{AstNode, AstNodeList}; use biome_string_case::StrLikeExtension; @@ -102,6 +103,7 @@ declare_lint_rule! { name: "noSvgWithoutTitle", language: "jsx", recommended: true, + severity: Severity::Error, } } @@ -131,7 +133,7 @@ impl Rule for NoSvgWithoutTitle { let jsx_element = node.parent::()?; if let AnyJsxElement::JsxOpeningElement(_) = node { let has_valid_title = has_valid_title_element(&jsx_element.children()); - if has_valid_title.map_or(false, |bool| bool) { + if has_valid_title.is_some_and(|bool| bool) { return None; } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_alt_text.rs b/crates/biome_js_analyze/src/lint/a11y/use_alt_text.rs index 6cbd3c6e98c8..31195e82349c 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_alt_text.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_alt_text.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::{fmt::Display, fmt::Formatter, markup}; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, static_value::StaticValue, TextRange}; use biome_rowan::AstNode; @@ -51,6 +52,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("alt-text")], recommended: true, + severity: Severity::Error, } } @@ -97,24 +99,33 @@ impl Rule for UseAltText { if !opening_element.has_accessible_child() { return Some(( ValidatedElement::Object, - element.syntax().text_range(), + element.syntax().text_range_with_trivia(), )); } } AnyJsxElement::JsxSelfClosingElement(_) => { - return Some((ValidatedElement::Object, element.syntax().text_range())); + return Some(( + ValidatedElement::Object, + element.syntax().text_range_with_trivia(), + )); } } } } "img" => { if !has_alt && !has_aria_label && !has_aria_labelledby && !aria_hidden { - return Some((ValidatedElement::Img, element.syntax().text_range())); + return Some(( + ValidatedElement::Img, + element.syntax().text_range_with_trivia(), + )); } } "area" => { if !has_alt && !has_aria_label && !has_aria_labelledby && !aria_hidden { - return Some((ValidatedElement::Area, element.syntax().text_range())); + return Some(( + ValidatedElement::Area, + element.syntax().text_range_with_trivia(), + )); } } "input" => { @@ -124,7 +135,10 @@ impl Rule for UseAltText { && !has_aria_labelledby && !aria_hidden { - return Some((ValidatedElement::Input, element.syntax().text_range())); + return Some(( + ValidatedElement::Input, + element.syntax().text_range_with_trivia(), + )); } } _ => {} @@ -149,17 +163,17 @@ impl Rule for UseAltText { fn has_type_image_attribute(element: &AnyJsxElement) -> bool { element .find_attribute_by_name("type") - .map_or(false, |attribute| { + .is_some_and(|attribute| { attribute .as_static_value() - .map_or(false, |value| value.text() == "image") + .is_some_and(|value| value.text() == "image") }) } fn has_valid_alt_text(element: &AnyJsxElement) -> bool { element .find_attribute_by_name("alt") - .map_or(false, |attribute| { + .is_some_and(|attribute| { if attribute.initializer().is_none() { return false; } @@ -167,27 +181,26 @@ fn has_valid_alt_text(element: &AnyJsxElement) -> bool { attribute .as_static_value() .map_or(true, |value| !value.is_null_or_undefined()) - && !element.has_trailing_spread_prop(&attribute) }) } fn has_valid_label(element: &AnyJsxElement, name_to_lookup: &str) -> bool { element .find_attribute_by_name(name_to_lookup) - .map_or(false, |attribute| { + .is_some_and(|attribute| { if attribute.initializer().is_none() { return false; } attribute.as_static_value().map_or(true, |value| { !value.is_null_or_undefined() && value.is_not_string_constant("") - }) && !element.has_trailing_spread_prop(&attribute) + }) }) } fn is_aria_hidden(element: &AnyJsxElement) -> bool { element .find_attribute_by_name("aria-hidden") - .map_or(false, |attribute| { + .is_some_and(|attribute| { attribute .as_static_value() .map_or(true, |value| match value { diff --git a/crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs b/crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs index 7763b08eb85b..49d2f778d3ed 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::JsxElement; use biome_rowan::{AstNode, BatchMutationExt}; @@ -69,6 +70,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("anchor-has-content")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -107,9 +109,10 @@ impl Rule for UseAnchorContent { fn diagnostic(ctx: &RuleContext, _state: &Self::State) -> Option { let range = match ctx.query() { - AnyJsxElement::JsxOpeningElement(node) => { - node.parent::()?.syntax().text_range() - } + AnyJsxElement::JsxOpeningElement(node) => node + .parent::()? + .syntax() + .text_range_with_trivia(), AnyJsxElement::JsxSelfClosingElement(node) => node.syntax().text_trimmed_range(), }; Some(RuleDiagnostic::new( @@ -158,7 +161,7 @@ fn has_valid_anchor_content(node: &AnyJsxElement) -> bool { .is_some() || node .find_attribute_by_name("children") - .map_or(false, |attribute| { + .is_some_and(|attribute| { if attribute.initializer().is_none() { return false; } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_aria_activedescendant_with_tabindex.rs b/crates/biome_js_analyze/src/lint/a11y/use_aria_activedescendant_with_tabindex.rs index dfe5475e00ff..234832c9a00e 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_aria_activedescendant_with_tabindex.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_aria_activedescendant_with_tabindex.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::{ jsx_attribute, jsx_attribute_initializer_clause, jsx_attribute_list, jsx_ident, jsx_name, jsx_string, jsx_string_literal, token, @@ -53,6 +54,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("aria-activedescendant-has-tabindex")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_aria_props_for_role.rs b/crates/biome_js_analyze/src/lint/a11y/use_aria_props_for_role.rs index cb8f03ca8f91..cd4d81c90388 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_aria_props_for_role.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_aria_props_for_role.rs @@ -2,6 +2,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::JsxAttribute; use biome_rowan::{AstNode, TokenText}; @@ -44,6 +45,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("role-has-required-aria-props")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_button_type.rs b/crates/biome_js_analyze/src/lint/a11y/use_button_type.rs index 228b78ce8697..aeecdd9bddf8 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_button_type.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_button_type.rs @@ -3,6 +3,7 @@ use crate::services::semantic::Semantic; use biome_analyze::RuleSource; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsxElementName, JsCallExpression, JsxAttribute, JsxOpeningElement, JsxSelfClosingElement, TextRange, @@ -42,6 +43,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReact("button-has-type")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_focusable_interactive.rs b/crates/biome_js_analyze/src/lint/a11y/use_focusable_interactive.rs index a7b586b9db1a..97f07ba7838a 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_focusable_interactive.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_focusable_interactive.rs @@ -1,6 +1,7 @@ use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsxAttributeValue}; use biome_rowan::AstNode; @@ -43,6 +44,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("interactive-supports-focus")], recommended: true, + severity: Severity::Error, } } @@ -66,7 +68,7 @@ impl Rule for UseFocusableInteractive { if attribute_has_interactive_role(&role_attribute_value)? && tabindex_attribute.is_none() { - return Some(role_attribute_value.text()); + return Some(role_attribute_value.to_trimmed_string()); } } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_heading_content.rs b/crates/biome_js_analyze/src/lint/a11y/use_heading_content.rs index 1320fc28fdff..61baf2003a91 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_heading_content.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_heading_content.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, JsxElement}; use biome_rowan::AstNode; @@ -61,6 +62,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("heading-has-content")], recommended: true, + severity: Severity::Error, } } @@ -105,9 +107,10 @@ impl Rule for UseHeadingContent { fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { let range = match ctx.query() { - AnyJsxElement::JsxOpeningElement(node) => { - node.parent::()?.syntax().text_range() - } + AnyJsxElement::JsxOpeningElement(node) => node + .parent::()? + .syntax() + .text_range_with_trivia(), AnyJsxElement::JsxSelfClosingElement(node) => node.syntax().text_trimmed_range(), }; Some(RuleDiagnostic::new( @@ -128,7 +131,7 @@ fn has_valid_heading_content(node: &AnyJsxElement) -> bool { .is_some() || node .find_attribute_by_name("children") - .map_or(false, |attribute| { + .is_some_and(|attribute| { if attribute.initializer().is_none() { return false; } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_html_lang.rs b/crates/biome_js_analyze/src/lint/a11y/use_html_lang.rs index 42a5c76d84fc..e296d32951e2 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_html_lang.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_html_lang.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, TextRange}; use biome_rowan::AstNode; @@ -60,6 +61,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("html-has-lang")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_iframe_title.rs b/crates/biome_js_analyze/src/lint/a11y/use_iframe_title.rs index 80637dfa9c5f..dedf754934fc 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_iframe_title.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_iframe_title.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::AstNode; @@ -66,6 +67,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("iframe-has-title")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_key_with_click_events.rs b/crates/biome_js_analyze/src/lint/a11y/use_key_with_click_events.rs index bf16bed34034..365fbc893077 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_key_with_click_events.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_key_with_click_events.rs @@ -4,6 +4,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{jsx_ext::AnyJsxElement, AnyJsxAttribute, AnyJsxElementName}; use biome_rowan::AstNode; use biome_string_case::StrLikeExtension; @@ -63,6 +64,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("click-events-have-key-events")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_key_with_mouse_events.rs b/crates/biome_js_analyze/src/lint/a11y/use_key_with_mouse_events.rs index 559f52e11097..4631a968fa59 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_key_with_mouse_events.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_key_with_mouse_events.rs @@ -2,6 +2,7 @@ use crate::services::semantic::Semantic; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::{markup, MarkupBuf}; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::AstNode; @@ -45,6 +46,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("mouse-events-have-key-events")], recommended: true, + severity: Severity::Error, } } @@ -106,9 +108,9 @@ impl Rule for UseKeyWithMouseEvents { fn has_valid_focus_attributes(elem: &AnyJsxElement) -> bool { if let Some(on_mouse_over_attribute) = elem.find_attribute_by_name("onMouseOver") { if !elem.has_trailing_spread_prop(&on_mouse_over_attribute) { - return elem.find_attribute_by_name("onFocus").map_or(false, |it| { + return elem.find_attribute_by_name("onFocus").is_some_and(|it| { !it.as_static_value() - .map_or(false, |value| value.is_null_or_undefined()) + .is_some_and(|value| value.is_null_or_undefined()) }); } } @@ -118,9 +120,9 @@ fn has_valid_focus_attributes(elem: &AnyJsxElement) -> bool { fn has_valid_blur_attributes(elem: &AnyJsxElement) -> bool { if let Some(on_mouse_attribute) = elem.find_attribute_by_name("onMouseOut") { if !elem.has_trailing_spread_prop(&on_mouse_attribute) { - return elem.find_attribute_by_name("onBlur").map_or(false, |it| { + return elem.find_attribute_by_name("onBlur").is_some_and(|it| { !it.as_static_value() - .map_or(false, |value| value.is_null_or_undefined()) + .is_some_and(|value| value.is_null_or_undefined()) }); } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_media_caption.rs b/crates/biome_js_analyze/src/lint/a11y/use_media_caption.rs index 463352a2b404..f95a008b3f08 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_media_caption.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_media_caption.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::{AnyJsxChild, JsxElement, TextRange}; use biome_rowan::AstNode; @@ -37,6 +38,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("media-has-caption")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs b/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs index feca101c0e92..2cd2934fd6fd 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs @@ -3,6 +3,7 @@ use biome_analyze::{ }; use biome_aria_metadata::AriaRole; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsxAttribute, JsxOpeningElement}; use biome_rowan::AstNode; @@ -46,6 +47,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("prefer-tag-over-role")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_valid_anchor.rs b/crates/biome_js_analyze/src/lint/a11y/use_valid_anchor.rs index 1b5a1af4bd3d..887952ff6895 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_valid_anchor.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_valid_anchor.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::{markup, MarkupBuf}; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, TextRange}; @@ -77,6 +78,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("anchor-is-valid")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_props.rs b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_props.rs index d8322cbecf83..3629df982de3 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_props.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_props.rs @@ -5,6 +5,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::AriaAttribute; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_js_syntax::JsxAttribute; use biome_rowan::{AstNode, AstNodeList, BatchMutationExt}; @@ -32,6 +33,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("aria-props")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_role.rs b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_role.rs index 64495ad70bb6..546730caf5c2 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_role.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_role.rs @@ -5,6 +5,7 @@ use biome_analyze::{ use biome_aria_metadata::AriaRole; use biome_console::markup; use biome_deserialize_macros::Deserializable; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, BatchMutationExt}; use serde::{Deserialize, Serialize}; @@ -69,6 +70,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("aria-role")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_values.rs b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_values.rs index abd7b18bf9f1..957c233b8e17 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_values.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_valid_aria_values.rs @@ -4,6 +4,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::{AriaAttribute, AriaValueType}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsSyntaxToken, JsxAttribute, TextRange}; use biome_rowan::AstNode; @@ -53,6 +54,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("aria-proptypes")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/a11y/use_valid_lang.rs b/crates/biome_js_analyze/src/lint/a11y/use_valid_lang.rs index 63c9f9cb9933..cf53f4780568 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_valid_lang.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_valid_lang.rs @@ -2,6 +2,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_aria_metadata::{is_valid_country, is_valid_language}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::jsx_ext::AnyJsxElement; use biome_rowan::{AstNode, TextRange}; declare_lint_rule! { @@ -34,6 +35,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintJsxA11y("lang")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/complexity.rs b/crates/biome_js_analyze/src/lint/complexity.rs index ecbfc5aad927..4566cb40a969 100644 --- a/crates/biome_js_analyze/src/lint/complexity.rs +++ b/crates/biome_js_analyze/src/lint/complexity.rs @@ -35,4 +35,5 @@ pub mod use_optional_chain; pub mod use_regex_literals; pub mod use_simple_number_keys; pub mod use_simplified_logic_expression; -declare_lint_group! { pub Complexity { name : "complexity" , rules : [self :: no_banned_types :: NoBannedTypes , self :: no_empty_type_parameters :: NoEmptyTypeParameters , self :: no_excessive_cognitive_complexity :: NoExcessiveCognitiveComplexity , self :: no_excessive_nested_test_suites :: NoExcessiveNestedTestSuites , self :: no_extra_boolean_cast :: NoExtraBooleanCast , self :: no_for_each :: NoForEach , self :: no_multiple_spaces_in_regular_expression_literals :: NoMultipleSpacesInRegularExpressionLiterals , self :: no_static_only_class :: NoStaticOnlyClass , self :: no_this_in_static :: NoThisInStatic , self :: no_useless_catch :: NoUselessCatch , self :: no_useless_constructor :: NoUselessConstructor , self :: no_useless_empty_export :: NoUselessEmptyExport , self :: no_useless_fragments :: NoUselessFragments , self :: no_useless_label :: NoUselessLabel , self :: no_useless_lone_block_statements :: NoUselessLoneBlockStatements , self :: no_useless_rename :: NoUselessRename , self :: no_useless_string_concat :: NoUselessStringConcat , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_useless_ternary :: NoUselessTernary , self :: no_useless_this_alias :: NoUselessThisAlias , self :: no_useless_type_constraint :: NoUselessTypeConstraint , self :: no_useless_undefined_initialization :: NoUselessUndefinedInitialization , self :: no_void :: NoVoid , self :: no_with :: NoWith , self :: use_arrow_function :: UseArrowFunction , self :: use_date_now :: UseDateNow , self :: use_flat_map :: UseFlatMap , self :: use_literal_keys :: UseLiteralKeys , self :: use_optional_chain :: UseOptionalChain , self :: use_regex_literals :: UseRegexLiterals , self :: use_simple_number_keys :: UseSimpleNumberKeys , self :: use_simplified_logic_expression :: UseSimplifiedLogicExpression ,] } } +pub mod use_while; +declare_lint_group! { pub Complexity { name : "complexity" , rules : [self :: no_banned_types :: NoBannedTypes , self :: no_empty_type_parameters :: NoEmptyTypeParameters , self :: no_excessive_cognitive_complexity :: NoExcessiveCognitiveComplexity , self :: no_excessive_nested_test_suites :: NoExcessiveNestedTestSuites , self :: no_extra_boolean_cast :: NoExtraBooleanCast , self :: no_for_each :: NoForEach , self :: no_multiple_spaces_in_regular_expression_literals :: NoMultipleSpacesInRegularExpressionLiterals , self :: no_static_only_class :: NoStaticOnlyClass , self :: no_this_in_static :: NoThisInStatic , self :: no_useless_catch :: NoUselessCatch , self :: no_useless_constructor :: NoUselessConstructor , self :: no_useless_empty_export :: NoUselessEmptyExport , self :: no_useless_fragments :: NoUselessFragments , self :: no_useless_label :: NoUselessLabel , self :: no_useless_lone_block_statements :: NoUselessLoneBlockStatements , self :: no_useless_rename :: NoUselessRename , self :: no_useless_string_concat :: NoUselessStringConcat , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_useless_ternary :: NoUselessTernary , self :: no_useless_this_alias :: NoUselessThisAlias , self :: no_useless_type_constraint :: NoUselessTypeConstraint , self :: no_useless_undefined_initialization :: NoUselessUndefinedInitialization , self :: no_void :: NoVoid , self :: no_with :: NoWith , self :: use_arrow_function :: UseArrowFunction , self :: use_date_now :: UseDateNow , self :: use_flat_map :: UseFlatMap , self :: use_literal_keys :: UseLiteralKeys , self :: use_optional_chain :: UseOptionalChain , self :: use_regex_literals :: UseRegexLiterals , self :: use_simple_number_keys :: UseSimpleNumberKeys , self :: use_simplified_logic_expression :: UseSimplifiedLogicExpression , self :: use_while :: UseWhile ,] } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_banned_types.rs b/crates/biome_js_analyze/src/lint/complexity/no_banned_types.rs index 1e92856a6508..f9bac6b1580b 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_banned_types.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_banned_types.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ JsReferenceIdentifier, JsSyntaxKind, TextRange, TsIntersectionTypeElementList, TsObjectType, @@ -93,6 +94,7 @@ declare_lint_rule! { language: "ts", sources: &[RuleSource::EslintTypeScript("ban-types")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_empty_type_parameters.rs b/crates/biome_js_analyze/src/lint/complexity/no_empty_type_parameters.rs index c9157cffc961..218d151789ae 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_empty_type_parameters.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_empty_type_parameters.rs @@ -1,6 +1,7 @@ use biome_analyze::Ast; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsSyntaxKind, TsTypeParameters}; use biome_rowan::{AstNode, AstSeparatedList, SyntaxNodeOptionExt}; @@ -39,6 +40,7 @@ declare_lint_rule! { name: "noEmptyTypeParameters", language: "ts", recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_excessive_nested_test_suites.rs b/crates/biome_js_analyze/src/lint/complexity/no_excessive_nested_test_suites.rs index adfebd34ca68..bbeb6f21594b 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_excessive_nested_test_suites.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_excessive_nested_test_suites.rs @@ -1,8 +1,9 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, AddVisitor, Phases, QueryMatch, Queryable, Rule, - RuleDiagnostic, RuleSource, RuleSourceKind, ServiceBag, Visitor, VisitorContext, + RuleDiagnostic, RuleDomain, RuleSource, RuleSourceKind, ServiceBag, Visitor, VisitorContext, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsCallExpression, JsLanguage, JsStaticMemberExpression}; use biome_rowan::{AstNode, Language, SyntaxNode, SyntaxNodeOptionExt, TextRange, WalkEvent}; @@ -56,8 +57,10 @@ declare_lint_rule! { name: "noExcessiveNestedTestSuites", language: "js", recommended: true, + severity: Severity::Error, sources: &[RuleSource::EslintJest("max-nested-describe")], source_kind: RuleSourceKind::SameLogic, + domains: &[RuleDomain::Test], } } @@ -151,11 +154,11 @@ fn is_member(call: &JsCallExpression) -> bool { call.syntax() .parent() .kind() - .map_or(false, JsStaticMemberExpression::can_cast) + .is_some_and(JsStaticMemberExpression::can_cast) || call .callee() .map(|callee| callee.syntax().kind()) - .map_or(false, JsStaticMemberExpression::can_cast) + .is_ok_and(JsStaticMemberExpression::can_cast) } // Declare a query match struct type containing a JavaScript function node diff --git a/crates/biome_js_analyze/src/lint/complexity/no_extra_boolean_cast.rs b/crates/biome_js_analyze/src/lint/complexity/no_extra_boolean_cast.rs index c3d43fd1e6f8..6d28b2f1ea24 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_extra_boolean_cast.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_extra_boolean_cast.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ is_in_boolean_context, is_negation, AnyJsExpression, JsCallArgumentList, JsCallArguments, JsCallExpression, JsNewExpression, JsSyntaxNode, JsUnaryOperator, @@ -59,6 +60,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-extra-boolean-cast")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_for_each.rs b/crates/biome_js_analyze/src/lint/complexity/no_for_each.rs index 01827652aaec..df8cdab7a91e 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_for_each.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_for_each.rs @@ -2,8 +2,11 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; -use biome_deserialize::{DeserializableValidator, DeserializationDiagnostic}; +use biome_deserialize::{ + DeserializableValidator, DeserializationContext, DeserializationDiagnostic, +}; use biome_deserialize_macros::Deserializable; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsExpression, AnyJsMemberExpression, JsCallExpression}; use biome_rowan::{AstNode, AstSeparatedList, TextRange}; use serde::{Deserialize, Serialize}; @@ -97,6 +100,7 @@ declare_lint_rule! { RuleSource::Clippy("needless_for_each"), ], recommended: true, + severity: Severity::Error, } } @@ -176,17 +180,17 @@ pub struct NoForEachOptions { impl DeserializableValidator for NoForEachOptions { fn validate( &mut self, + ctx: &mut impl DeserializationContext, _name: &str, range: TextRange, - diagnostics: &mut Vec, ) -> bool { if self .allowed_identifiers .iter() .any(|identifier| identifier.is_empty() || identifier.contains('.')) { - diagnostics - .push( + ctx + .report( DeserializationDiagnostic::new(markup!( "'allowedIdentifiers'"" does not accept empty values or values with dots." )) diff --git a/crates/biome_js_analyze/src/lint/complexity/no_multiple_spaces_in_regular_expression_literals.rs b/crates/biome_js_analyze/src/lint/complexity/no_multiple_spaces_in_regular_expression_literals.rs index c77c145887a0..79d1153409c1 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_multiple_spaces_in_regular_expression_literals.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_multiple_spaces_in_regular_expression_literals.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsRegexLiteralExpression, JsSyntaxKind, JsSyntaxToken, TextRange, TextSize}; use biome_rowan::BatchMutationExt; use std::{fmt::Write, ops::Range}; @@ -50,6 +51,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-regex-spaces")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_static_only_class.rs b/crates/biome_js_analyze/src/lint/complexity/no_static_only_class.rs index e64740ec11b3..59b284e1c677 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_static_only_class.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_static_only_class.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsClass, AnyJsClassMember, JsGetterClassMember, JsMethodClassMember, JsPropertyClassMember, JsSetterClassMember, TsGetterSignatureClassMember, TsIndexSignatureClassMember, @@ -99,6 +100,7 @@ declare_lint_rule! { RuleSource::EslintUnicorn("no-static-only-class"), ], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_this_in_static.rs b/crates/biome_js_analyze/src/lint/complexity/no_this_in_static.rs index 767d4dffd0ee..0f1ae1da17c1 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_this_in_static.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_this_in_static.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsClass, AnyJsClassMember, AnyJsExpression, JsArrowFunctionExpression, JsSuperExpression, @@ -82,6 +83,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::EslintMysticatea("no-this-in-static")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_catch.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_catch.rs index 5c0ff325f82c..3d0be8e5c226 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_catch.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_catch.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{AnyJsTryStatement, JsStatementList, TextRange}; use biome_rowan::{AstNode, AstNodeList, BatchMutationExt}; @@ -69,6 +70,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-useless-catch")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -106,7 +108,7 @@ impl Rule for NoUselessCatch { .argument() .ok()? .as_js_identifier_expression()? - .text(); + .to_trimmed_string(); if throw_ident.eq(catch_err_name) { Some(js_throw_statement.syntax().text_trimmed_range()) diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_constructor.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_constructor.rs index 1c1bc0be579f..1b302e17ac1d 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_constructor.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_constructor.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsCallArgument, AnyJsClass, AnyJsConstructorParameter, AnyJsFormalParameter, JsCallExpression, JsConstructorClassMember, @@ -122,6 +123,7 @@ declare_lint_rule! { RuleSource::EslintTypeScript("no-useless-constructor"), ], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_empty_export.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_empty_export.rs index 9b0591e76666..b88ed330cc1f 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_empty_export.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_empty_export.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsModuleItem, JsExport, JsModuleItemList, JsSyntaxToken}; use biome_rowan::{AstNode, AstSeparatedList, BatchMutationExt}; @@ -46,6 +47,7 @@ declare_lint_rule! { language: "ts", sources: &[RuleSource::EslintTypeScript("no-useless-empty-export")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_fragments.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_fragments.rs index 71153ac9e7ad..dcd927bdc637 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_fragments.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_fragments.rs @@ -4,6 +4,7 @@ use crate::JsRuleAction; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::{ js_string_literal_expression, jsx_expression_attribute_value, jsx_expression_child, jsx_string, jsx_string_literal, jsx_tag_expression, token, JsxExpressionChildBuilder, @@ -65,6 +66,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReact("jsx-no-useless-fragment")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -73,6 +75,7 @@ declare_lint_rule! { pub enum NoUselessFragmentsState { Empty, Child(AnyJsxChild), + Children(JsxChildList), } declare_node_union! { @@ -126,46 +129,53 @@ impl Rule for NoUselessFragments { let mut in_jsx_attr_expr = false; let mut in_js_logical_expr = false; let mut in_jsx_expr = false; + let mut in_jsx_list = false; match node { NoUselessFragmentsQuery::JsxFragment(fragment) => { - let parents_where_fragments_must_be_preserved = node.syntax().parent().map_or( - false, - |parent| match JsxTagExpression::try_cast(parent.clone()) { - Ok(parent) => parent - .syntax() - .parent() - .and_then(|parent| { - if JsxExpressionAttributeValue::can_cast(parent.kind()) { - in_jsx_attr_expr = true; - } - if JsLogicalExpression::can_cast(parent.kind()) { - in_js_logical_expr = true; - } - if JsxExpressionChild::can_cast(parent.kind()) { - in_jsx_expr = true; - } - match JsParenthesizedExpression::try_cast(parent) { - Ok(parenthesized_expression) => { - parenthesized_expression.syntax().parent() + let parents_where_fragments_must_be_preserved = + node.syntax().parent().is_some_and(|parent| { + match JsxTagExpression::try_cast(parent.clone()) { + Ok(parent) => parent + .syntax() + .parent() + .and_then(|parent| { + if JsxExpressionAttributeValue::can_cast(parent.kind()) { + in_jsx_attr_expr = true; + } + if JsLogicalExpression::can_cast(parent.kind()) { + in_js_logical_expr = true; + } + if JsxExpressionChild::can_cast(parent.kind()) { + in_jsx_expr = true; } - Err(parent) => Some(parent), + match JsParenthesizedExpression::try_cast(parent) { + Ok(parenthesized_expression) => { + parenthesized_expression.syntax().parent() + } + Err(parent) => Some(parent), + } + }) + .is_some_and(|parent| { + matches!( + parent.kind(), + JsSyntaxKind::JS_RETURN_STATEMENT + | JsSyntaxKind::JS_INITIALIZER_CLAUSE + | JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION + | JsSyntaxKind::JS_FUNCTION_EXPRESSION + | JsSyntaxKind::JS_FUNCTION_DECLARATION + | JsSyntaxKind::JS_PROPERTY_OBJECT_MEMBER + ) + }), + Err(_) => { + if JsxChildList::try_cast(parent.clone()).is_ok() { + in_jsx_list = true; + false + } else { + JsxAttributeInitializerClause::try_cast(parent.clone()).is_ok() } - }) - .map_or(false, |parent| { - matches!( - parent.kind(), - JsSyntaxKind::JS_RETURN_STATEMENT - | JsSyntaxKind::JS_INITIALIZER_CLAUSE - | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION - | JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION - | JsSyntaxKind::JS_FUNCTION_EXPRESSION - | JsSyntaxKind::JS_FUNCTION_DECLARATION - | JsSyntaxKind::JS_PROPERTY_OBJECT_MEMBER - ) - }), - Err(_) => JsxAttributeInitializerClause::try_cast(parent.clone()).is_ok(), - }, - ); + } + } + }); let child_list = fragment.children(); @@ -197,7 +207,7 @@ impl Rule for NoUselessFragments { JsSyntaxKind::JSX_TEXT => { // We need to whitespaces and newlines from the original string. // Since in the JSX newlines aren't trivia, we require to allocate a string to trim from those characters. - let original_text = child.text(); + let original_text = child.to_trimmed_string(); let child_text = original_text.trim(); if (in_jsx_expr || in_js_logical_expr) @@ -238,7 +248,7 @@ impl Rule for NoUselessFragments { None } } - _ => None, + _ => in_jsx_list.then_some(NoUselessFragmentsState::Children(child_list)), } } else { None @@ -297,24 +307,32 @@ impl Rule for NoUselessFragments { let node = ctx.query(); let mut mutation = ctx.root().begin(); - let is_in_jsx_attr = node.syntax().grand_parent().map_or(false, |parent| { - JsxExpressionAttributeValue::can_cast(parent.kind()) - }); + let is_in_jsx_attr = node + .syntax() + .grand_parent() + .is_some_and(|parent| JsxExpressionAttributeValue::can_cast(parent.kind())); let is_in_list = node .syntax() .parent() - .map_or(false, |parent| JsxChildList::can_cast(parent.kind())); + .is_some_and(|parent| JsxChildList::can_cast(parent.kind())); if is_in_list { - let new_child = match state { - NoUselessFragmentsState::Empty => None, - NoUselessFragmentsState::Child(child) => Some(child.clone()), - }; - - if let Some(new_child) = new_child { - node.replace_node(&mut mutation, new_child); - } else { - node.remove_node_from_list(&mut mutation); + match state { + NoUselessFragmentsState::Child(child) => { + node.replace_node(&mut mutation, child.clone()); + } + NoUselessFragmentsState::Children(children) => { + if let Some(old_children) = node + .syntax() + .parent() + .and_then(|parent| JsxChildList::cast(parent.clone())) + { + mutation.replace_node(old_children, children.clone()); + } + } + _ => { + node.remove_node_from_list(&mut mutation); + } } } else if let Some(parent) = node.parent::() { let parent = match parent.parent::() { @@ -329,7 +347,12 @@ impl Rule for NoUselessFragments { | JsSyntaxKind::JSX_ELEMENT | JsSyntaxKind::JSX_EXPRESSION_CHILD | JsSyntaxKind::JSX_FRAGMENT => true, - JsSyntaxKind::JSX_TEXT => !child.syntax().text().to_string().trim().is_empty(), + JsSyntaxKind::JSX_TEXT => !child + .syntax() + .text_with_trivia() + .to_string() + .trim() + .is_empty(), _ => false, }); diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_label.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_label.rs index e57020687cbc..31173c63494c 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_label.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_label.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsStatement, JsLabeledStatement, JsSyntaxKind}; use crate::JsRuleAction; @@ -37,6 +38,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-extra-label")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_lone_block_statements.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_lone_block_statements.rs index 05cf42d5d849..3eb9a1ac0175 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_lone_block_statements.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_lone_block_statements.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsStatement, AnyJsSwitchClause, JsBlockStatement, JsFileSource, JsLabeledStatement, @@ -47,6 +48,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-lone-blocks")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } @@ -128,12 +130,12 @@ impl Rule for NoUselessLoneBlockStatements { let mut mutation = ctx.root().begin(); mutation.replace_node_discard_trivia(stmts_list, new_stmts_list); - return Some(JsRuleAction::new( + Some(JsRuleAction::new( ctx.metadata().action_category(ctx.category(), ctx.group()), ctx.metadata().applicability(), markup! { "Remove redundant block." }.to_owned(), mutation, - )); + )) } } @@ -179,5 +181,5 @@ fn is_not_var_declaration(variable: &JsVariableStatement) -> bool { variable .declaration() .ok() - .map_or(false, |decl| !decl.is_var()) + .is_some_and(|decl| !decl.is_var()) } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_rename.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_rename.rs index 20f300fd530a..7984536a1b90 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_rename.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_rename.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsExportNamedSpecifier, AnyJsNamedImportSpecifier, AnyJsObjectBindingPatternMember, @@ -62,6 +63,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-useless-rename")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_string_concat.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_string_concat.rs index 9c4f092e4502..a5834bb4473b 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_string_concat.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_string_concat.rs @@ -357,7 +357,7 @@ fn extract_string_value(expression: &Option) -> Option String::new(), |acc, element| { if let Some(chunk) = element.as_js_template_chunk_element() { - return acc + chunk.text().as_str(); + return acc + chunk.to_trimmed_string().as_str(); } acc }, diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_switch_case.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_switch_case.rs index a357b5323351..e6979800e838 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_switch_case.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_switch_case.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsSwitchClause, JsCaseClause, JsDefaultClause}; use biome_rowan::{AstNode, AstNodeList, BatchMutationExt, Direction}; @@ -62,6 +63,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::EslintUnicorn("no-useless-switch-case")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_ternary.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_ternary.rs index 64934d58d2dc..0c479e7a12c2 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_ternary.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_ternary.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsExpression, AnyJsLiteralExpression, JsConditionalExpression, JsSyntaxKind, @@ -57,6 +58,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-unneeded-ternary")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -204,12 +206,12 @@ impl Rule for NoUselessTernary { } mutation.replace_element(node.clone().into(), new_node.into()); - return Some(JsRuleAction::new( + Some(JsRuleAction::new( ctx.metadata().action_category(ctx.category(), ctx.group()), ctx.metadata().applicability(), markup! { "Remove the conditional expression with" }.to_owned(), mutation, - )); + )) } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_this_alias.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_this_alias.rs index 71422314538f..b33ae1743a15 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_this_alias.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_this_alias.rs @@ -6,6 +6,7 @@ use biome_analyze::{ RuleSourceKind, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_semantic::ReferencesExtensions; use biome_js_syntax::{ @@ -57,6 +58,7 @@ declare_lint_rule! { sources: &[RuleSource::EslintTypeScript("no-this-alias")], source_kind: RuleSourceKind::Inspired, recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_type_constraint.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_type_constraint.rs index 211a35047220..9885f41c074b 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_type_constraint.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_type_constraint.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ @@ -81,6 +82,7 @@ declare_lint_rule! { language: "ts", sources: &[RuleSource::EslintTypeScript("no-unnecessary-type-constraint")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/no_useless_undefined_initialization.rs b/crates/biome_js_analyze/src/lint/complexity/no_useless_undefined_initialization.rs index b3c423b7fb66..80da2b60c6bc 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_useless_undefined_initialization.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_useless_undefined_initialization.rs @@ -94,7 +94,7 @@ impl Rule for NoUselessUndefinedInitialization { if keyword.is_undefined() { let decl_range = initializer.range(); - let Some(binding_name) = decl.id().ok().map(|id| id.text()) else { + let Some(binding_name) = decl.id().ok().map(|id| id.to_trimmed_string()) else { continue; }; signals.push((binding_name.into(), decl_range)); diff --git a/crates/biome_js_analyze/src/lint/complexity/no_with.rs b/crates/biome_js_analyze/src/lint/complexity/no_with.rs index 7fc07b32b91b..8c667b8ff6ae 100644 --- a/crates/biome_js_analyze/src/lint/complexity/no_with.rs +++ b/crates/biome_js_analyze/src/lint/complexity/no_with.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::JsWithStatement; use biome_rowan::AstNode; @@ -28,6 +29,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-with")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/use_arrow_function.rs b/crates/biome_js_analyze/src/lint/complexity/use_arrow_function.rs index 4ee4bbf71554..e4e095eecb3d 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_arrow_function.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_arrow_function.rs @@ -4,6 +4,7 @@ use biome_analyze::{ Rule, RuleDiagnostic, RuleSource, RuleSourceKind, ServiceBag, Visitor, VisitorContext, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsExpression, AnyJsFunctionBody, AnyJsStatement, JsConstructorClassMember, JsFileSource, @@ -72,6 +73,7 @@ declare_lint_rule! { sources: &[RuleSource::Eslint("prefer-arrow-callback")], source_kind: RuleSourceKind::Inspired, recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/use_flat_map.rs b/crates/biome_js_analyze/src/lint/complexity/use_flat_map.rs index 39aac2d1f351..3bdec8b1c3d1 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_flat_map.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_flat_map.rs @@ -2,6 +2,7 @@ use crate::JsRuleAction; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::{ident, js_name}; use biome_js_syntax::{AnyJsExpression, AnyJsMemberExpression, AnyJsName, JsCallExpression}; use biome_rowan::{AstNode, AstSeparatedList, BatchMutationExt}; @@ -39,6 +40,7 @@ declare_lint_rule! { RuleSource::Clippy("map_flatten"), ], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs index 83735f02bad9..087f4f49d715 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsAssignment, AnyJsComputedMember, AnyJsMemberExpression, AnyJsName, AnyJsObjectMemberName, @@ -52,6 +53,7 @@ declare_lint_rule! { RuleSource::EslintTypeScript("dot-notation") ], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs index aaec4858d714..85f76fadaa66 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_optional_chain.rs @@ -1,6 +1,7 @@ use biome_analyze::RuleSource; use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsExpression, AnyJsMemberExpression, AnyJsName, JsLogicalExpression, JsLogicalOperator, @@ -76,6 +77,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::EslintTypeScript("prefer-optional-chain")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -527,8 +529,8 @@ impl LogicalAndChain { // they should be considered different even if `main_value_token` // and `branch_value_token` are the same. // Therefore, we need to check their arguments here. - if main_call_expression_args.args().text() - != branch_call_expression_args.args().text() + if main_call_expression_args.args().to_trimmed_string() + != branch_call_expression_args.args().to_trimmed_string() { return Ok(LogicalAndChainOrdering::Different); } @@ -751,7 +753,7 @@ impl LogicalOrLikeChain { /// ### Example /// `(foo ?? {}).bar` is inside `((foo ?? {}).bar || {}).baz;` fn is_inside_another_chain(&self) -> bool { - LogicalOrLikeChain::get_chain_parent(&self.member).map_or(false, |parent| { + LogicalOrLikeChain::get_chain_parent(&self.member).is_some_and(|parent| { parent .as_js_logical_expression() .filter(|parent_expression| { @@ -783,7 +785,7 @@ impl LogicalOrLikeChain { // E.g. (((foo || {}))).bar; let object = object.omit_parentheses(); if let AnyJsExpression::JsLogicalExpression(logical) = object { - let is_valid_operator = logical.operator().map_or(false, |operator| { + let is_valid_operator = logical.operator().is_ok_and(|operator| { matches!( operator, JsLogicalOperator::NullishCoalescing | JsLogicalOperator::LogicalOr diff --git a/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs b/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs index 99f456b5fa71..9a7d3a292646 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::js_regex_literal_expression; use biome_js_semantic::SemanticModel; use biome_js_syntax::{ @@ -48,6 +49,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("prefer-regex-literals")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/complexity/use_simple_number_keys.rs b/crates/biome_js_analyze/src/lint/complexity/use_simple_number_keys.rs index 1799f677ddcc..084390c445f1 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_simple_number_keys.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_simple_number_keys.rs @@ -2,6 +2,7 @@ use crate::JsRuleAction; use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsObjectMember, JsLiteralMemberName, JsObjectExpression, JsSyntaxKind, JsSyntaxToken, @@ -47,6 +48,7 @@ declare_lint_rule! { name: "useSimpleNumberKeys", language: "js", recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/style/use_while.rs b/crates/biome_js_analyze/src/lint/complexity/use_while.rs similarity index 97% rename from crates/biome_js_analyze/src/lint/style/use_while.rs rename to crates/biome_js_analyze/src/lint/complexity/use_while.rs index 519169be2c8a..42302cd1d4fa 100644 --- a/crates/biome_js_analyze/src/lint/style/use_while.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_while.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{AnyJsStatement, JsForStatement, T}; use biome_rowan::{trim_leading_trivia_pieces, AstNode, BatchMutationExt}; @@ -41,7 +42,8 @@ declare_lint_rule! { version: "1.0.0", name: "useWhile", language: "js", - recommended: true, + recommended: false, + severity: Severity::Warning, sources: &[RuleSource::EslintSonarJs("prefer-while")], fix_kind: FixKind::Safe, } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_children_prop.rs b/crates/biome_js_analyze/src/lint/correctness/no_children_prop.rs index 1408ec243157..28f9fd241eac 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_children_prop.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_children_prop.rs @@ -3,6 +3,7 @@ use crate::services::semantic::Semantic; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsCallExpression, JsxAttribute}; use biome_rowan::{declare_node_union, AstNode, TextRange}; declare_lint_rule! { @@ -28,6 +29,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReact("no-children-prop")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_const_assign.rs b/crates/biome_js_analyze/src/lint/correctness/no_const_assign.rs index bf90297cd7f8..7d23cd0719ed 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_const_assign.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_const_assign.rs @@ -3,6 +3,7 @@ use crate::JsRuleAction; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::{self}; use biome_js_syntax::binding_ext::AnyJsBindingDeclaration; use biome_js_syntax::{JsIdentifierAssignment, JsSyntaxKind}; @@ -52,6 +53,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-const-assign")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_constant_condition.rs b/crates/biome_js_analyze/src/lint/correctness/no_constant_condition.rs index 3a0eb139b7ec..5dd3d1fd1304 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_constant_condition.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_constant_condition.rs @@ -1,14 +1,14 @@ -use crate::{services::semantic::Semantic, utils::rename::RenamableNode}; +use crate::ast_utils::is_constant_condition; +use crate::services::semantic::Semantic; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; -use biome_js_semantic::SemanticModel; +use biome_diagnostics::Severity; use biome_js_syntax::{ - AnyJsArrayElement, AnyJsExpression, AnyJsLiteralExpression, AnyJsStatement, - AnyJsTemplateElement, JsAssignmentOperator, JsConditionalExpression, JsDoWhileStatement, - JsForStatement, JsFunctionDeclaration, JsFunctionExpression, JsIfStatement, JsLogicalOperator, - JsStatementList, JsSyntaxKind, JsUnaryOperator, JsWhileStatement, JsYieldExpression, TextRange, + AnyJsExpression, AnyJsStatement, JsConditionalExpression, JsDoWhileStatement, JsForStatement, + JsFunctionDeclaration, JsFunctionExpression, JsIfStatement, JsStatementList, JsSyntaxKind, + JsWhileStatement, JsYieldExpression, TextRange, }; -use biome_rowan::{declare_node_union, AstNode, AstSeparatedList}; +use biome_rowan::{declare_node_union, AstNode}; declare_lint_rule! { /// Disallow constant expressions in conditions @@ -85,6 +85,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-constant-condition")], recommended: true, + severity: Severity::Error, } } @@ -228,300 +229,3 @@ fn has_valid_yield_expression(stmt: &AnyJsStatement) -> Option { } } } - -fn is_constant_condition( - test: AnyJsExpression, - in_boolean_position: bool, - model: &SemanticModel, -) -> Option<()> { - use AnyJsExpression::*; - - match test.omit_parentheses() { - AnyJsLiteralExpression(_) - | JsObjectExpression(_) - | JsFunctionExpression(_) - | JsArrowFunctionExpression(_) - | JsClassExpression(_) => Some(()), - JsUnaryExpression(node) => { - use JsUnaryOperator::*; - - let op = node.operator().ok()?; - if op == Void || op == Typeof && in_boolean_position { - return Some(()); - } - if op == LogicalNot { - return is_constant_condition(node.argument().ok()?, true, model); - } - is_constant_condition(node.argument().ok()?, false, model) - } - JsBinaryExpression(node) => is_constant_condition(node.left().ok()?, false, model) - .and_then(|_| is_constant_condition(node.right().ok()?, false, model)), - JsLogicalExpression(node) => { - let left = node.left().ok()?; - let right = node.right().ok()?; - let op = node.operator().ok()?; - let is_left_constant = - is_constant_condition(left.clone(), in_boolean_position, model).is_some(); - let is_right_constant = - is_constant_condition(right.clone(), in_boolean_position, model).is_some(); - - let is_left_short_circuit = is_left_constant && is_logical_identity(left, op); - let is_right_short_circuit = - in_boolean_position && is_right_constant && is_logical_identity(right, op); - - if (is_left_constant && is_right_constant) - || is_left_short_circuit - || is_right_short_circuit - { - Some(()) - } else { - None - } - } - JsSequenceExpression(node) => { - is_constant_condition(node.right().ok()?, in_boolean_position, model) - } - JsIdentifierExpression(node) => { - if node.name().ok()?.binding(model).is_some() { - // This is any_js_stmt edge case. Modern browsers don't allow to redeclare `undefined` but ESLint handle this so we do - return None; - } - let is_named_undefined = node.name().ok()?.is_undefined(); - is_named_undefined.then_some(()) - } - JsArrayExpression(node) => { - if !in_boolean_position { - node.elements() - .into_iter() - .all(|js_statement| { - if let Ok(element) = js_statement { - match element { - AnyJsArrayElement::JsArrayHole(_) => true, - AnyJsArrayElement::JsSpread(node) => { - if let Ok(argument) = node.argument() { - is_constant_condition(argument, in_boolean_position, model) - .is_some() - } else { - false - } - } - _ => element - .as_any_js_expression() - .and_then(|node| { - is_constant_condition(node.clone(), false, model) - }) - .is_some(), - } - } else { - false - } - }) - .then_some(()) - } else { - Some(()) - } - } - JsNewExpression(_) => in_boolean_position.then_some(()), - JsCallExpression(node) => { - if node.has_callee("Boolean") { - let callee = node.callee().ok()?; - let ident = callee.as_js_identifier_expression()?.name().ok()?; - let binding = ident.binding(model); - if binding.is_some() { - return None; - } - - let args = node.arguments().ok()?.args(); - if args.is_empty() { - return Some(()); - } - return is_constant_condition( - args.first()?.ok()?.as_any_js_expression()?.clone(), - true, - model, - ); - } - - None - } - JsAssignmentExpression(node) => { - use JsAssignmentOperator::*; - - let operator = node.operator().ok()?; - if operator == Assign { - return is_constant_condition(node.right().ok()?, in_boolean_position, model); - } - - if matches!(operator, LogicalOrAssign | LogicalAndAssign) && in_boolean_position { - let new_op = match operator { - LogicalAndAssign => JsLogicalOperator::LogicalAnd, - LogicalOrAssign => JsLogicalOperator::LogicalOr, - _ => unreachable!(), - }; - - return is_logical_identity(node.right().ok()?, new_op).then_some(()); - } - None - } - JsTemplateExpression(node) => { - let is_tag = node.tag().is_some(); - let elements = node.elements(); - let has_truthy_quasi = !is_tag - && elements.clone().into_iter().any(|element| match element { - AnyJsTemplateElement::JsTemplateChunkElement(element) => { - if let Ok(quasi) = element.template_chunk_token() { - !quasi.text_trimmed().is_empty() - } else { - false - } - } - AnyJsTemplateElement::JsTemplateElement(_) => false, - }); - if has_truthy_quasi && in_boolean_position { - return Some(()); - } - - elements - .into_iter() - .all(|element| match element { - AnyJsTemplateElement::JsTemplateChunkElement(_) => !is_tag, - AnyJsTemplateElement::JsTemplateElement(element) => { - if let Ok(expr) = element.expression() { - is_constant_condition(expr, false, model).is_some() - } else { - false - } - } - }) - .then_some(()) - } - _ => None, - } -} - -fn is_logical_identity(node: AnyJsExpression, operator: JsLogicalOperator) -> bool { - use AnyJsExpression::*; - use JsLogicalOperator::*; - match node.omit_parentheses() { - AnyJsLiteralExpression(node) => { - let boolean_value = get_boolean_value(&node); - operator == LogicalOr && boolean_value || (operator == LogicalAnd && !boolean_value) - } - JsUnaryExpression(node) => { - if operator != LogicalAnd { - return false; - } - - if let Ok(node_operator) = node.operator() { - node_operator == JsUnaryOperator::Void - } else { - false - } - } - JsLogicalExpression(node) => { - if let Ok(node_operator) = node.operator() { - // handles `any_js_stmt && false || b` - // `false` is an identity element of `&&` but not `||` - // so the logical identity of the whole expression can not be defined. - if operator != node_operator { - return false; - } - - let is_left_logical_identify = node - .left() - .ok() - .map_or(false, |left| is_logical_identity(left, operator)); - if is_left_logical_identify { - return true; - } - - node.right() - .ok() - .map_or(false, |right| is_logical_identity(right, operator)) - } else { - false - } - } - JsAssignmentExpression(node) => { - if let Ok(node_operator) = node.operator() { - if let Ok(right) = node.right() { - let is_valid_logical_assignment = match node_operator { - JsAssignmentOperator::LogicalAndAssign - if operator == JsLogicalOperator::LogicalAnd => - { - true - } - JsAssignmentOperator::LogicalOrAssign - if operator == JsLogicalOperator::LogicalOr => - { - true - } - _ => false, - }; - - is_valid_logical_assignment && is_logical_identity(right, operator) - } else { - false - } - } else { - false - } - } - _ => false, - } -} - -fn get_boolean_value(node: &AnyJsLiteralExpression) -> bool { - use AnyJsLiteralExpression::*; - match node { - JsRegexLiteralExpression(_) => true, - _ => node - .as_static_value() - .map_or(false, |value| !value.is_falsy()), - } -} - -#[cfg(test)] -mod tests { - use biome_js_parser::JsParserOptions; - use biome_js_syntax::{AnyJsLiteralExpression, JsFileSource}; - use biome_rowan::SyntaxNodeCast; - - use super::get_boolean_value; - - fn assert_boolean_value(code: &str, value: bool) { - let source = biome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); - - if source.has_errors() { - panic!("syntax error") - } - - let literal_expression = source - .syntax() - .descendants() - .find_map(|js_statement| js_statement.cast::()); - - assert_eq!( - get_boolean_value(&literal_expression.expect("Not found AnyLiteralExpression.")), - value - ); - } - #[test] - fn test_get_boolean_value() { - assert_boolean_value("false", false); - assert_boolean_value("0", false); - assert_boolean_value("-0", false); - assert_boolean_value("0n", false); - assert_boolean_value("let any_js_stmt =\"\"", false); - assert_boolean_value("let any_js_stmt = ''", false); - assert_boolean_value("null", false); - - assert_boolean_value("true", true); - assert_boolean_value("let any_js_stmt = \"0\"", true); - assert_boolean_value("let any_js_stmt = \"false\"", true); - assert_boolean_value("-42", true); - assert_boolean_value("12n", true); - assert_boolean_value("3.14", true); - assert_boolean_value("-3.14", true); - } -} diff --git a/crates/biome_js_analyze/src/lint/correctness/no_constant_math_min_max_clamp.rs b/crates/biome_js_analyze/src/lint/correctness/no_constant_math_min_max_clamp.rs index 05326cc2eaa2..e5507b158a97 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_constant_math_min_max_clamp.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_constant_math_min_max_clamp.rs @@ -94,7 +94,7 @@ impl Rule for NoConstantMathMinMaxClamp { ).detail( state.0.range(), markup! { - "It always evaluates to "{state.0.text()}"." + "It always evaluates to "{state.0.to_trimmed_string()}"." } ) ) @@ -109,7 +109,7 @@ impl Rule for NoConstantMathMinMaxClamp { Some(JsRuleAction::new( ctx.metadata().action_category(ctx.category(), ctx.group()), ctx.metadata().applicability(), - markup! {"Swap "{state.0.text()}" with "{state.1.text()}"."} + markup! {"Swap "{state.0.to_trimmed_string()}" with "{state.1.to_trimmed_string()}"."} .to_owned(), mutation, )) diff --git a/crates/biome_js_analyze/src/lint/correctness/no_constructor_return.rs b/crates/biome_js_analyze/src/lint/correctness/no_constructor_return.rs index 84603d1a4f65..c71c12424438 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_constructor_return.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_constructor_return.rs @@ -2,6 +2,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::RuleSource; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsConstructorClassMember, JsReturnStatement}; use biome_rowan::AstNode; @@ -58,6 +59,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-constructor-return")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_empty_character_class_in_regex.rs b/crates/biome_js_analyze/src/lint/correctness/no_empty_character_class_in_regex.rs index 63317a26c4d0..483c4509c989 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_empty_character_class_in_regex.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_empty_character_class_in_regex.rs @@ -4,6 +4,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::JsRegexLiteralExpression; use biome_rowan::{TextRange, TextSize}; @@ -46,6 +47,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-empty-character-class")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_empty_pattern.rs b/crates/biome_js_analyze/src/lint/correctness/no_empty_pattern.rs index 29f0acfebda0..681eda4cc1b5 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_empty_pattern.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_empty_pattern.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsArrayBindingPattern, JsObjectBindingPattern}; use biome_rowan::{declare_node_union, AstNode, AstSeparatedList}; @@ -40,6 +41,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-empty-pattern")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_flat_map_identity.rs b/crates/biome_js_analyze/src/lint/correctness/no_flat_map_identity.rs index 36bb5305c337..3fea7ee759a9 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_flat_map_identity.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_flat_map_identity.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make::{ident, js_call_argument_list, js_call_arguments, js_name, token}; use biome_js_syntax::{ AnyJsExpression, AnyJsFunctionBody, AnyJsMemberExpression, AnyJsName, AnyJsStatement, @@ -39,6 +40,7 @@ declare_lint_rule! { name: "noFlatMapIdentity", language: "js", recommended: true, + severity: Severity::Error, sources: &[RuleSource::Clippy("flat_map_identity")], fix_kind: FixKind::Safe, } @@ -72,12 +74,12 @@ impl Rule for NoFlatMapIdentity { AnyJsExpression::JsArrowFunctionExpression(arg) => { let parameter: String = match arg.parameters().ok()? { biome_js_syntax::AnyJsArrowFunctionParameters::AnyJsBinding(p) => { - p.text().trim_matches(['(', ')']).to_owned() + p.to_trimmed_string().trim_matches(['(', ')']).to_owned() } biome_js_syntax::AnyJsArrowFunctionParameters::JsParameters(p) => { if p.items().len() == 1 { if let Some(param) = p.items().into_iter().next() { - param.ok()?.text() + param.ok()?.to_trimmed_string() } else { return None; } @@ -88,7 +90,9 @@ impl Rule for NoFlatMapIdentity { }; let function_body: String = match arg.body().ok()? { - AnyJsFunctionBody::AnyJsExpression(body) => body.omit_parentheses().text(), + AnyJsFunctionBody::AnyJsExpression(body) => { + body.omit_parentheses().to_trimmed_string() + } AnyJsFunctionBody::JsFunctionBody(body) => { let mut statement = body.statements().into_iter(); match statement.next() { @@ -99,7 +103,7 @@ impl Rule for NoFlatMapIdentity { else { return None; }; - return_statement.name().ok()?.text() + return_statement.name().ok()?.to_trimmed_string() } _ => return None, } @@ -108,7 +112,7 @@ impl Rule for NoFlatMapIdentity { (parameter, function_body) } AnyJsExpression::JsFunctionExpression(arg) => { - let function_parameter = arg.parameters().ok()?.text(); + let function_parameter = arg.parameters().ok()?.to_trimmed_string(); let function_parameter = function_parameter.trim_matches(['(', ')']).to_owned(); let mut statement = arg.body().ok()?.statements().into_iter(); @@ -118,7 +122,10 @@ impl Rule for NoFlatMapIdentity { else { return None; }; - (function_parameter, return_statement.name().ok()?.text()) + ( + function_parameter, + return_statement.name().ok()?.to_trimmed_string(), + ) } else { return None; } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_global_object_calls.rs b/crates/biome_js_analyze/src/lint/correctness/no_global_object_calls.rs index f239d57ab161..379a5187537d 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_global_object_calls.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_global_object_calls.rs @@ -1,6 +1,7 @@ use crate::services::semantic::Semantic; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression}; use biome_rowan::{declare_node_union, SyntaxResult, TextRange}; use std::{fmt::Display, str::FromStr}; @@ -89,6 +90,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-obj-calls")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_inner_declarations.rs b/crates/biome_js_analyze/src/lint/correctness/no_inner_declarations.rs index 228f5baf4df2..4205c3e25935 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_inner_declarations.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_inner_declarations.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsDeclaration, JsFileSource, JsStatementList, JsSyntaxKind}; use biome_rowan::AstNode; @@ -93,6 +94,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-inner-declarations")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_invalid_builtin_instantiation.rs b/crates/biome_js_analyze/src/lint/correctness/no_invalid_builtin_instantiation.rs index f478a92b18d2..18f8149a5ea0 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_invalid_builtin_instantiation.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_invalid_builtin_instantiation.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ global_identifier, static_value::StaticValue, AnyJsExpression, JsCallExpression, @@ -79,6 +80,7 @@ declare_lint_rule! { RuleSource::Eslint("no-new-native-nonconstructor"), ], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_invalid_constructor_super.rs b/crates/biome_js_analyze/src/lint/correctness/no_invalid_constructor_super.rs index 8d824099200f..1eaa4a8f3681 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_invalid_constructor_super.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_invalid_constructor_super.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::{markup, MarkupBuf}; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsClass, AnyJsExpression, JsAssignmentOperator, JsConstructorClassMember, JsLogicalOperator, }; @@ -51,6 +52,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("constructor-super")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs b/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs index b884ea27d0ab..a51691d1797f 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs @@ -1,6 +1,7 @@ use crate::{services::control_flow::AnyJsControlFlowRoot, services::semantic::SemanticServices}; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ binding_ext::{AnyJsBindingDeclaration, AnyJsIdentifierBinding}, AnyJsExportNamedSpecifier, AnyJsIdentifierUsage, @@ -66,6 +67,7 @@ declare_lint_rule! { RuleSource::EslintTypeScript("no-use-before-define"), ], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_nonoctal_decimal_escape.rs b/crates/biome_js_analyze/src/lint/correctness/no_nonoctal_decimal_escape.rs index 1a30d2843bbb..54b5e4cc1b73 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_nonoctal_decimal_escape.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_nonoctal_decimal_escape.rs @@ -3,6 +3,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::JsStringLiteralExpression; use biome_rowan::{AstNode, BatchMutationExt, TextRange}; @@ -57,6 +58,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-nonoctal-decimal-escape")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_precision_loss.rs b/crates/biome_js_analyze/src/lint/correctness/no_precision_loss.rs index 1c654443cdf1..fbae5bf70f8d 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_precision_loss.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_precision_loss.rs @@ -4,6 +4,7 @@ use std::ops::RangeInclusive; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::numbers::split_into_radix_and_number; use biome_js_syntax::JsNumberLiteralExpression; @@ -55,6 +56,7 @@ declare_lint_rule! { RuleSource::Clippy("lossy_float_literal") ], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_render_return_value.rs b/crates/biome_js_analyze/src/lint/correctness/no_render_return_value.rs index c2f1924ce89e..9322517ebb52 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_render_return_value.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_render_return_value.rs @@ -3,6 +3,7 @@ use crate::services::semantic::Semantic; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsCallExpression, JsExpressionStatement}; use biome_rowan::AstNode; @@ -34,6 +35,7 @@ declare_lint_rule! { name: "noRenderReturnValue", language: "jsx", recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_self_assign.rs b/crates/biome_js_analyze/src/lint/correctness/no_self_assign.rs index c8e060e8f9d4..fd709abd8646 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_self_assign.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_self_assign.rs @@ -1,6 +1,7 @@ use biome_analyze::RuleSource; use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ inner_string_text, AnyJsArrayAssignmentPatternElement, AnyJsArrayElement, AnyJsAssignment, AnyJsAssignmentPattern, AnyJsExpression, AnyJsLiteralExpression, AnyJsName, @@ -72,6 +73,7 @@ declare_lint_rule! { RuleSource::Clippy("self_assignment"), ], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_setter_return.rs b/crates/biome_js_analyze/src/lint/correctness/no_setter_return.rs index ef38fa71d773..4f642bb0b09e 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_setter_return.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_setter_return.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsReturnStatement, JsSetterClassMember, JsSetterObjectMember}; use biome_rowan::{declare_node_union, AstNode}; @@ -70,6 +71,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-setter-return")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs b/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs index 4cb4055db236..cc02c8301e7e 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::*; use biome_rowan::{declare_node_union, AstNode, AstSeparatedList, BatchMutationExt}; @@ -39,6 +40,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Clippy("match_str_case_mismatch")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -221,7 +223,7 @@ impl<'a> CharCaseIterator<'a> { CharCaseIterator { iter: s.chars() } } } -impl<'a> Iterator for CharCaseIterator<'a> { +impl Iterator for CharCaseIterator<'_> { type Item = StringCase; fn next(&mut self) -> Option { while let Some(c) = self.iter.next() { diff --git a/crates/biome_js_analyze/src/lint/correctness/no_switch_declarations.rs b/crates/biome_js_analyze/src/lint/correctness/no_switch_declarations.rs index 5e1df3247ba6..45251b54cc8d 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_switch_declarations.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_switch_declarations.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_syntax::{ AnyJsDeclaration, AnyJsStatement, AnyJsSwitchClause, JsVariableStatement, TriviaPieceKind, T, @@ -74,6 +75,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-case-declarations")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_undeclared_dependencies.rs b/crates/biome_js_analyze/src/lint/correctness/no_undeclared_dependencies.rs index b02f4fd35d2b..7b60d2982518 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_undeclared_dependencies.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_undeclared_dependencies.rs @@ -1,11 +1,12 @@ -use std::path::Path; - use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; -use biome_deserialize::{Deserializable, DeserializableType}; +use biome_deserialize::{ + Deserializable, DeserializableType, DeserializableValue, DeserializationContext, +}; use biome_deserialize_macros::Deserializable; use biome_js_syntax::{AnyJsImportClause, AnyJsImportLike}; use biome_rowan::AstNode; +use camino::Utf8Path; use crate::{globals::is_node_builtin_module, services::manifest::Manifest}; @@ -105,14 +106,14 @@ impl Default for DependencyAvailability { impl Deserializable for DependencyAvailability { fn deserialize( - value: &impl biome_deserialize::DeserializableValue, + ctx: &mut impl DeserializationContext, + value: &impl DeserializableValue, name: &str, - diagnostics: &mut Vec, ) -> Option { Some(if value.visitable_type()? == DeserializableType::Bool { - Self::Bool(bool::deserialize(value, name, diagnostics)?) + Self::Bool(bool::deserialize(ctx, value, name)?) } else { - Self::Patterns(Deserializable::deserialize(value, name, diagnostics)?) + Self::Patterns(Deserializable::deserialize(ctx, value, name)?) }) } } @@ -162,7 +163,7 @@ impl schemars::JsonSchema for DependencyAvailability { } impl DependencyAvailability { - fn is_available(&self, path: &Path) -> bool { + fn is_available(&self, path: &Utf8Path) -> bool { match self { Self::Bool(b) => *b, Self::Patterns(globs) => { @@ -268,11 +269,23 @@ impl Rule for NoUndeclaredDependencies { is_optional_dependency_available, } = state; + let Some(package_path) = ctx.package_path.as_ref() else { + return Some(RuleDiagnostic::new( + rule_category!(), + ctx.query().range(), + markup! { + "Dependency "{package_name}" cannot be verified because no package.json file was found." + }, + )); + }; + + let manifest_path = package_path.clone(); + let diag = RuleDiagnostic::new( rule_category!(), ctx.query().range(), markup! { - "The current dependency isn't specified in your package.json." + "Dependency "{package_name}" isn't specified in "{manifest_path.as_str()}"." }, ); diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unnecessary_continue.rs b/crates/biome_js_analyze/src/lint/correctness/no_unnecessary_continue.rs index 57e855415911..8c1d4900f89f 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unnecessary_continue.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unnecessary_continue.rs @@ -1,5 +1,6 @@ use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{JsContinueStatement, JsLabeledStatement, JsSyntaxKind, JsSyntaxNode}; use biome_rowan::{AstNode, BatchMutationExt}; @@ -73,6 +74,7 @@ declare_lint_rule! { name: "noUnnecessaryContinue", language: "js", recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unreachable.rs b/crates/biome_js_analyze/src/lint/correctness/no_unreachable.rs index dd5acfdb6705..26269fe19c03 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unreachable.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unreachable.rs @@ -1,10 +1,12 @@ use std::{cmp::Ordering, collections::VecDeque, num::NonZeroU32, vec::IntoIter}; +use crate::services::control_flow::{ControlFlowGraph, JsControlFlowGraph}; use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use biome_control_flow::{ builder::{BlockId, ROOT_BLOCK_ID}, ExceptionHandler, ExceptionHandlerKind, Instruction, InstructionKind, }; +use biome_diagnostics::Severity; use biome_js_syntax::{ JsBlockStatement, JsCaseClause, JsDefaultClause, JsDoWhileStatement, JsForInStatement, JsForOfStatement, JsForStatement, JsFunctionBody, JsIfStatement, JsLabeledStatement, @@ -15,8 +17,6 @@ use biome_rowan::{declare_node_union, AstNode, NodeOrToken}; use roaring::bitmap::RoaringBitmap; use rustc_hash::FxHashMap; -use crate::services::control_flow::{ControlFlowGraph, JsControlFlowGraph}; - declare_lint_rule! { /// Disallow unreachable code /// @@ -53,6 +53,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-unreachable")], recommended: true, + severity: Severity::Error, } } @@ -712,7 +713,7 @@ impl UnreachableRanges { if statements.text_trimmed_range().is_empty() { vec![] } else { - vec![statements.text_range()] + vec![statements.text_range_with_trivia()] } } @@ -723,7 +724,9 @@ impl UnreachableRanges { .into_iter() .filter_map(|declarator| match declarator { Ok(declarator) => match declarator.initializer()?.expression() { - Ok(expression) => Some(Ok(expression.syntax().text_range())), + Ok(expression) => { + Some(Ok(expression.syntax().text_range_with_trivia())) + } Err(err) => Some(Err(err)), }, Err(err) => Some(Err(err)), @@ -732,69 +735,80 @@ impl UnreachableRanges { .ok()? } JsControlFlowNode::JsLabeledStatement(stmt) => { - vec![stmt.body().ok()?.syntax().text_range()] + vec![stmt.body().ok()?.syntax().text_range_with_trivia()] } JsControlFlowNode::JsDoWhileStatement(stmt) => vec![ - stmt.body().ok()?.syntax().text_range(), - stmt.test().ok()?.syntax().text_range(), + stmt.body().ok()?.syntax().text_range_with_trivia(), + stmt.test().ok()?.syntax().text_range_with_trivia(), ], JsControlFlowNode::JsForInStatement(stmt) => vec![ - stmt.initializer().ok()?.syntax().text_range(), - stmt.body().ok()?.syntax().text_range(), + stmt.initializer().ok()?.syntax().text_range_with_trivia(), + stmt.body().ok()?.syntax().text_range_with_trivia(), ], JsControlFlowNode::JsForOfStatement(stmt) => vec![ - stmt.initializer().ok()?.syntax().text_range(), - stmt.body().ok()?.syntax().text_range(), + stmt.initializer().ok()?.syntax().text_range_with_trivia(), + stmt.body().ok()?.syntax().text_range_with_trivia(), ], JsControlFlowNode::JsForStatement(stmt) => { let mut res = Vec::new(); if let Some(initializer) = stmt.initializer() { - res.push(initializer.syntax().text_range()); + res.push(initializer.syntax().text_range_with_trivia()); } if let Some(test) = stmt.test() { - res.push(test.syntax().text_range()); + res.push(test.syntax().text_range_with_trivia()); } if let Some(update) = stmt.update() { - res.push(update.syntax().text_range()); + res.push(update.syntax().text_range_with_trivia()); } - res.push(stmt.body().ok()?.syntax().text_range()); + res.push(stmt.body().ok()?.syntax().text_range_with_trivia()); res } JsControlFlowNode::JsIfStatement(stmt) => { let mut res = vec![ - stmt.test().ok()?.syntax().text_range(), - stmt.consequent().ok()?.syntax().text_range(), + stmt.test().ok()?.syntax().text_range_with_trivia(), + stmt.consequent().ok()?.syntax().text_range_with_trivia(), ]; if let Some(else_clause) = stmt.else_clause() { - res.push(else_clause.alternate().ok()?.syntax().text_range()); + res.push( + else_clause + .alternate() + .ok()? + .syntax() + .text_range_with_trivia(), + ); } res } JsControlFlowNode::JsSwitchStatement(stmt) => { - let mut res = vec![stmt.discriminant().ok()?.syntax().text_range()]; + let mut res = vec![stmt.discriminant().ok()?.syntax().text_range_with_trivia()]; let cases = stmt.cases().into_syntax(); if !cases.text_trimmed_range().is_empty() { - res.push(cases.text_range()); + res.push(cases.text_range_with_trivia()); } res } JsControlFlowNode::JsTryStatement(stmt) => vec![ - stmt.body().ok()?.syntax().text_range(), - stmt.catch_clause().ok()?.body().ok()?.syntax().text_range(), + stmt.body().ok()?.syntax().text_range_with_trivia(), + stmt.catch_clause() + .ok()? + .body() + .ok()? + .syntax() + .text_range_with_trivia(), ], JsControlFlowNode::JsTryFinallyStatement(stmt) => { - let mut res = vec![stmt.body().ok()?.syntax().text_range()]; + let mut res = vec![stmt.body().ok()?.syntax().text_range_with_trivia()]; if let Some(catch_clause) = stmt.catch_clause() { - res.push(catch_clause.body().ok()?.syntax().text_range()); + res.push(catch_clause.body().ok()?.syntax().text_range_with_trivia()); } res.push( @@ -803,21 +817,21 @@ impl UnreachableRanges { .body() .ok()? .syntax() - .text_range(), + .text_range_with_trivia(), ); res } JsControlFlowNode::JsWhileStatement(stmt) => vec![ - stmt.test().ok()?.syntax().text_range(), - stmt.body().ok()?.syntax().text_range(), + stmt.test().ok()?.syntax().text_range_with_trivia(), + stmt.body().ok()?.syntax().text_range_with_trivia(), ], JsControlFlowNode::JsCaseClause(stmt) => { - let mut res = vec![stmt.test().ok()?.syntax().text_range()]; + let mut res = vec![stmt.test().ok()?.syntax().text_range_with_trivia()]; let consequent = stmt.consequent().into_syntax(); if !consequent.text_trimmed_range().is_empty() { - res.push(consequent.text_range()); + res.push(consequent.text_range_with_trivia()); } res @@ -827,7 +841,7 @@ impl UnreachableRanges { let consequent = stmt.consequent().into_syntax(); if !consequent.text_trimmed_range().is_empty() { - res.push(consequent.text_range()); + res.push(consequent.text_range_with_trivia()); } res @@ -838,7 +852,9 @@ impl UnreachableRanges { // Extend the range at the specific index to cover the whole parent node let entry = &mut self.ranges[next_index]; - entry.text_range = entry.text_range.cover(parent.syntax().text_range()); + entry.text_range = entry + .text_range + .cover(parent.syntax().text_range_with_trivia()); entry.text_trimmed_range = entry .text_trimmed_range .cover(parent.syntax().text_trimmed_range()); diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unreachable_super.rs b/crates/biome_js_analyze/src/lint/correctness/no_unreachable_super.rs index d088b9459b03..18ec892fced7 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unreachable_super.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unreachable_super.rs @@ -4,6 +4,7 @@ use biome_control_flow::{ builder::{BlockId, ROOT_BLOCK_ID}, ExceptionHandlerKind, InstructionKind, }; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsClass, AnyJsExpression, JsCallExpression, JsConstructorClassMember, JsSyntaxKind, JsThrowStatement, TextRange, WalkEvent, @@ -67,6 +68,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-this-before-super")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unsafe_finally.rs b/crates/biome_js_analyze/src/lint/correctness/no_unsafe_finally.rs index ac2d030884b2..90580bd2da85 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unsafe_finally.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unsafe_finally.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::*; use biome_rowan::{declare_node_union, AstNode}; @@ -130,6 +131,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-unsafe-finally")], recommended: true, + severity: Severity::Error, } } @@ -191,7 +193,7 @@ impl ControlFlowStatement { if parent .label_token() .ok() - .map_or(false, |it| it.text_trimmed() == label.text_trimmed()) + .is_some_and(|it| it.text_trimmed() == label.text_trimmed()) { is_label_inside_finally = true; } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unsafe_optional_chaining.rs b/crates/biome_js_analyze/src/lint/correctness/no_unsafe_optional_chaining.rs index a15637675d1a..8552ae38c6a2 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unsafe_optional_chaining.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unsafe_optional_chaining.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsAssignmentPattern, AnyJsBindingPattern, AnyJsOptionalChainExpression, JsArrayAssignmentPatternElement, JsAssignmentExpression, JsAwaitExpression, JsCallExpression, @@ -69,6 +70,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-unsafe-optional-chaining")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unused_labels.rs b/crates/biome_js_analyze/src/lint/correctness/no_unused_labels.rs index 17fc12908fe2..173c3288cea2 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unused_labels.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unused_labels.rs @@ -4,6 +4,7 @@ use biome_analyze::{ RuleSource, ServiceBag, Visitor, VisitorContext, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsStatement, JsBreakStatement, JsContinueStatement, JsFileSource, JsLabeledStatement, JsLanguage, TextRange, WalkEvent, @@ -64,6 +65,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("no-unused-labels")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Safe, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/no_void_elements_with_children.rs b/crates/biome_js_analyze/src/lint/correctness/no_void_elements_with_children.rs index e85651dfa7f4..43e2b004c4c7 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_void_elements_with_children.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_void_elements_with_children.rs @@ -4,6 +4,7 @@ use crate::JsRuleAction; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::{markup, MarkupBuf}; +use biome_diagnostics::Severity; use biome_js_factory::make::{jsx_attribute_list, jsx_self_closing_element}; use biome_js_syntax::{ AnyJsxAttribute, JsCallExpression, JsPropertyObjectMember, JsxAttribute, JsxElement, @@ -35,6 +36,7 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReact("void-dom-elements-no-children")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -43,27 +45,13 @@ declare_node_union! { pub NoVoidElementsWithChildrenQuery = JsxElement | JsCallExpression | JsxSelfClosingElement } +const VOID_ELEMENTS: [&str; 16] = [ + "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "menuitem", + "meta", "param", "source", "track", "wbr", +]; /// Returns true if the name of the element belong to a self-closing element fn is_void_dom_element(element_name: &str) -> bool { - matches!( - element_name, - "area" - | "base" - | "br" - | "col" - | "embed" - | "hr" - | "img" - | "input" - | "keygen" - | "link" - | "menuitem" - | "meta" - | "param" - | "source" - | "track" - | "wbr" - ) + VOID_ELEMENTS.contains(&element_name) } pub enum NoVoidElementsWithChildrenCause { diff --git a/crates/biome_js_analyze/src/lint/correctness/no_void_type_return.rs b/crates/biome_js_analyze/src/lint/correctness/no_void_type_return.rs index 1e32d40a6d89..884f5bbb365a 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_void_type_return.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_void_type_return.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsExpression, AnyTsReturnType, JsArrowFunctionExpression, JsFunctionDeclaration, JsFunctionExportDefaultDeclaration, JsFunctionExpression, JsGetterClassMember, @@ -90,6 +91,7 @@ declare_lint_rule! { name: "noVoidTypeReturn", language: "ts", recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs index 4e89988ad457..311d4b58393b 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs @@ -1,9 +1,11 @@ use crate::react::hooks::*; use crate::services::semantic::Semantic; use biome_analyze::RuleSource; -use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic}; +use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleDomain}; use biome_console::markup; -use biome_deserialize::{non_empty, DeserializableValidator, DeserializationDiagnostic}; +use biome_deserialize::{ + non_empty, DeserializableValidator, DeserializationContext, DeserializationDiagnostic, +}; use biome_deserialize_macros::Deserializable; use biome_js_semantic::{Capture, SemanticModel}; use biome_js_syntax::{ @@ -16,6 +18,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +use biome_diagnostics::Severity; #[cfg(feature = "schemars")] use schemars::JsonSchema; @@ -244,6 +247,8 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintReactHooks("exhaustive-deps")], recommended: true, + severity: Severity::Error, + domains: &[RuleDomain::React, RuleDomain::Next], } } @@ -352,15 +357,15 @@ pub struct Hook { impl DeserializableValidator for Hook { fn validate( &mut self, + ctx: &mut impl DeserializationContext, _name: &str, range: biome_rowan::TextRange, - diagnostics: &mut Vec, ) -> bool { match (self.closure_index, self.dependencies_index) { (Some(closure_index), Some(dependencies_index)) if closure_index == dependencies_index => { - diagnostics.push( + ctx.report( DeserializationDiagnostic::new(markup! { "closureIndex"" and ""dependenciesIndex"" may not be the same" }) @@ -506,7 +511,7 @@ fn capture_needs_to_be_in_the_dependency_list( | AnyJsBindingDeclaration::TsEnumMember(_) => false, // Function declarations are stable if ... AnyJsBindingDeclaration::JsFunctionDeclaration(declaration) => { - let declaration_range = declaration.syntax().text_range(); + let declaration_range = declaration.syntax().text_range_with_trivia(); // ... they are declared outside of the component function if component_function_range @@ -542,7 +547,7 @@ fn capture_needs_to_be_in_the_dependency_list( else { return false; }; - let declaration_range = declaration.syntax().text_range(); + let declaration_range = declaration.syntax().text_range_with_trivia(); // ... they are declared outside of the component function if component_function_range @@ -597,7 +602,7 @@ fn capture_needs_to_be_in_the_dependency_list( | AnyJsBindingDeclaration::JsArrayBindingPatternRestElement(_) | AnyJsBindingDeclaration::JsObjectBindingPatternProperty(_) | AnyJsBindingDeclaration::JsObjectBindingPatternRest(_) - | AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_) => true, + | AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_) => false, // This should be unreachable because of the test if the capture is imported AnyJsBindingDeclaration::JsShorthandNamedImportSpecifier(_) @@ -760,7 +765,7 @@ impl Rule for UseExhaustiveDependencies { } } - let component_function_range = component_function.text_range(); + let component_function_range = component_function.text_range_with_trivia(); let captures: Vec<_> = result .all_captures(model) @@ -934,13 +939,11 @@ impl Rule for UseExhaustiveDependencies { match dep { Fix::MissingDependenciesArray { function_name_range, - } => { - return Some(RuleDiagnostic::new( - rule_category!(), - function_name_range, - markup! {"This hook does not have a dependencies array"}, - )) - } + } => Some(RuleDiagnostic::new( + rule_category!(), + function_name_range, + markup! {"This hook does not have a dependencies array"}, + )), Fix::AddDependency { function_name_range, captures, diff --git a/crates/biome_js_analyze/src/lint/correctness/use_hook_at_top_level.rs b/crates/biome_js_analyze/src/lint/correctness/use_hook_at_top_level.rs index bb497acc7b9b..d2c87d26a639 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_hook_at_top_level.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_hook_at_top_level.rs @@ -1,15 +1,15 @@ use crate::react::hooks::{is_react_component, is_react_hook, is_react_hook_call}; use crate::services::semantic::{SemanticModelBuilderVisitor, SemanticServices}; -use biome_analyze::RuleSource; use biome_analyze::{ context::RuleContext, declare_lint_rule, AddVisitor, FromServices, MissingServicesDiagnostic, Phase, Phases, QueryMatch, Queryable, Rule, RuleDiagnostic, RuleKey, ServiceBag, Visitor, VisitorContext, VisitorFinishContext, }; +use biome_analyze::{RuleDomain, RuleSource}; use biome_console::markup; use biome_deserialize::{ - Deserializable, DeserializableTypes, DeserializableValue, DeserializationDiagnostic, - DeserializationVisitor, Text, + Deserializable, DeserializableTypes, DeserializableValue, DeserializationContext, + DeserializationDiagnostic, DeserializationVisitor, Text, }; use biome_js_semantic::{CallsExtensions, SemanticModel}; use biome_js_syntax::{ @@ -24,6 +24,7 @@ use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; +use biome_diagnostics::Severity; #[cfg(feature = "schemars")] use schemars::JsonSchema; @@ -69,7 +70,9 @@ declare_lint_rule! { name: "useHookAtTopLevel", language: "jsx", sources: &[RuleSource::EslintReactHooks("rules-of-hooks")], - recommended: false, + recommended: true, + severity: Severity::Error, + domains: &[RuleDomain::React, RuleDomain::Next], } } @@ -90,12 +93,15 @@ impl AnyJsFunctionOrMethod { fn name(&self) -> Option { match self { - AnyJsFunctionOrMethod::AnyJsFunction(function) => { - function.binding().as_ref().map(AnyJsBinding::text) - } - AnyJsFunctionOrMethod::JsMethodObjectMember(method) => { - method.name().ok().as_ref().map(AnyJsObjectMemberName::text) - } + AnyJsFunctionOrMethod::AnyJsFunction(function) => function + .binding() + .as_ref() + .map(AnyJsBinding::to_trimmed_string), + AnyJsFunctionOrMethod::JsMethodObjectMember(method) => method + .name() + .ok() + .as_ref() + .map(AnyJsObjectMemberName::to_trimmed_string), } } } @@ -291,7 +297,7 @@ impl Visitor for EarlyReturnDetectionVisitor { if let Some(entry) = self.stack.last_mut() { if JsReturnStatement::can_cast(node.kind()) { - entry.early_return = Some(node.text_range()); + entry.early_return = Some(node.text_range_with_trivia()); } else if let Some(call) = JsCallExpression::cast_ref(node) { if let Some(early_return) = entry.early_return { self.early_returns.insert(call.clone(), early_return); @@ -428,7 +434,7 @@ impl Rule for UseHookAtTopLevel { let mut calls = vec![root]; while let Some(CallPath { call, path }) = calls.pop() { - let range = call.syntax().text_range(); + let range = call.syntax().text_range_with_trivia(); let mut path = path.clone(); path.push(range); @@ -558,11 +564,11 @@ pub struct DeprecatedHooksOptions {} impl Deserializable for DeprecatedHooksOptions { fn deserialize( + ctx: &mut impl DeserializationContext, value: &impl DeserializableValue, name: &str, - diagnostics: &mut Vec, ) -> Option { - value.deserialize(DeprecatedHooksOptionsVisitor, name, diagnostics) + value.deserialize(ctx, DeprecatedHooksOptionsVisitor, name) } } @@ -575,19 +581,19 @@ impl DeserializationVisitor for DeprecatedHooksOptionsVisitor { fn visit_map( self, + ctx: &mut impl DeserializationContext, members: impl Iterator>, _range: TextRange, _name: &str, - diagnostics: &mut Vec, ) -> Option { const ALLOWED_KEYS: &[&str] = &["hooks"]; for (key, value) in members.flatten() { - let Some(key_text) = Text::deserialize(&key, "", diagnostics) else { + let Some(key_text) = Text::deserialize(ctx, &key, "") else { continue; }; match key_text.text() { "hooks" => { - diagnostics.push( + ctx.report( DeserializationDiagnostic::new_deprecated( key_text.text(), value.range() @@ -597,7 +603,7 @@ impl DeserializationVisitor for DeprecatedHooksOptionsVisitor { }) ); } - text => diagnostics.push(DeserializationDiagnostic::new_unknown_key( + text => ctx.report(DeserializationDiagnostic::new_unknown_key( text, key.range(), ALLOWED_KEYS, diff --git a/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs b/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs index a95205e70474..d78afb7997a5 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs @@ -149,7 +149,7 @@ impl Rule for UseImportExtensions { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); - let file_ext = ctx.file_path().extension().and_then(|ext| ext.to_str())?; + let file_ext = ctx.file_path().extension()?; let custom_suggested_imports = &ctx.options().suggested_extensions; @@ -224,7 +224,7 @@ fn get_extensionless_import( let has_query_or_hash = last_component .as_os_str() .to_str() - .map_or(false, |last| last.contains('?') || last.contains('#')); + .is_some_and(|last| last.contains('?') || last.contains('#')); if has_query_or_hash { return Some(UseImportExtensionsState { diff --git a/crates/biome_js_analyze/src/lint/correctness/use_is_nan.rs b/crates/biome_js_analyze/src/lint/correctness/use_is_nan.rs index 7c5a31e95e9a..152476b9e5db 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_is_nan.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_is_nan.rs @@ -1,6 +1,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_factory::make; use biome_js_semantic::SemanticModel; use biome_js_syntax::{ @@ -64,6 +65,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("use-isnan")], recommended: true, + severity: Severity::Error, fix_kind: FixKind::Unsafe, } } @@ -181,7 +183,7 @@ impl Rule for UseIsNan { call.into(), ); - return Some(JsRuleAction::new( + Some(JsRuleAction::new( ctx.metadata().action_category(ctx.category(), ctx.group()), ctx.metadata().applicability(), markup! { @@ -189,7 +191,7 @@ impl Rule for UseIsNan { } .to_owned(), mutation, - )); + )) } UseIsNanQuery::JsCaseClause(_) => None, UseIsNanQuery::JsSwitchStatement(_) => None, @@ -233,7 +235,7 @@ fn create_is_nan_expression(nan: AnyJsExpression) -> Option { .object() .ok()? .as_js_static_member_expression() - .is_some_and(|y| y.member().is_ok_and(|z| z.text() == "Number")); + .is_some_and(|y| y.member().is_ok_and(|z| z.to_trimmed_string() == "Number")); if !reference.is_global_this() && !reference.has_name("window") || number_identifier_exists diff --git a/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs b/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs index 53fb89b8da18..1a2b872b0e21 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs @@ -1,8 +1,9 @@ use crate::react::{is_react_call_api, ReactLibrary}; use crate::services::semantic::Semantic; -use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic}; +use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleDomain}; use biome_analyze::{RuleSource, RuleSourceKind}; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_semantic::SemanticModel; use biome_js_syntax::{ AnyJsExpression, AnyJsFunctionBody, AnyJsMemberExpression, AnyJsObjectMember, AnyJsxAttribute, @@ -42,6 +43,8 @@ declare_lint_rule! { sources: &[RuleSource::EslintReact("jsx-key")], source_kind: RuleSourceKind::SameLogic, recommended: true, + severity: Severity::Error, + domains: &[RuleDomain::React], } } @@ -324,7 +327,7 @@ fn handle_jsx_child(node: &AnyJsxChild, model: &SemanticModel) -> Option node .expression() - .map_or(false, |n| n.as_js_conditional_expression().is_some()), + .is_some_and(|n| n.as_js_conditional_expression().is_some()), _ => false, }); @@ -382,7 +385,7 @@ fn has_key_attribute(attributes: &JsxAttributeList) -> bool { // key must be statically provided, so no spread if let AnyJsxAttribute::JsxAttribute(attr) = attr { if let Ok(name) = attr.name() { - name.text() == "key" + name.to_trimmed_string() == "key" } else { false } diff --git a/crates/biome_js_analyze/src/lint/correctness/use_valid_for_direction.rs b/crates/biome_js_analyze/src/lint/correctness/use_valid_for_direction.rs index 92fadd3f2a34..c330afc0382e 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_valid_for_direction.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_valid_for_direction.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{ AnyJsExpression, JsAssignmentOperator, JsBinaryOperator, JsForStatement, JsIdentifierAssignment, JsIdentifierExpression, JsPostUpdateOperator, JsUnaryOperator, @@ -46,6 +47,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("for-direction")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/correctness/use_yield.rs b/crates/biome_js_analyze/src/lint/correctness/use_yield.rs index b7db065a26a5..23e8469feb3a 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_yield.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_yield.rs @@ -4,6 +4,7 @@ use biome_analyze::{ ServiceBag, Visitor, VisitorContext, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyFunctionLike, JsLanguage, JsYieldExpression, TextRange, WalkEvent}; use biome_rowan::{AstNode, AstNodeList, Language, SyntaxNode, TextSize}; @@ -42,6 +43,7 @@ declare_lint_rule! { language: "js", sources: &[RuleSource::Eslint("require-yield")], recommended: true, + severity: Severity::Error, } } diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 918634cb5b07..70c34d4fd202 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -4,20 +4,24 @@ use biome_analyze::declare_lint_group; pub mod no_common_js; +pub mod no_constant_binary_expression; pub mod no_document_cookie; pub mod no_document_import_in_page; pub mod no_duplicate_else_if; pub mod no_dynamic_namespace_import_access; pub mod no_enum; pub mod no_exported_imports; +pub mod no_floating_promises; pub mod no_global_dirname_filename; pub mod no_head_element; pub mod no_head_import_in_document; pub mod no_img_element; +pub mod no_import_cycles; pub mod no_irregular_whitespace; pub mod no_nested_ternary; pub mod no_noninteractive_element_interactions; pub mod no_octal_escape; +pub mod no_package_private_imports; pub mod no_process_env; pub mod no_process_global; pub mod no_restricted_imports; @@ -26,6 +30,8 @@ pub mod no_secrets; pub mod no_static_element_interactions; pub mod no_substr; pub mod no_template_curly_in_string; +pub mod no_ts_ignore; +pub mod no_unwanted_polyfillio; pub mod no_useless_escape_in_regex; pub mod no_useless_string_raw; pub mod no_useless_undefined; @@ -41,10 +47,9 @@ pub mod use_exports_last; pub mod use_google_font_display; pub mod use_google_font_preconnect; pub mod use_guard_for_in; -pub mod use_import_restrictions; pub mod use_parse_int_radix; pub mod use_sorted_classes; pub mod use_strict_mode; pub mod use_trim_start_end; pub mod use_valid_autocomplete; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_import_restrictions :: UseImportRestrictions , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_common_js.rs b/crates/biome_js_analyze/src/lint/nursery/no_common_js.rs index a6a2ee742598..bf1c3feb65fe 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_common_js.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_common_js.rs @@ -70,7 +70,7 @@ impl Rule for NoCommonJs { fn run(ctx: &RuleContext) -> Self::Signals { let file_ext = ctx.file_path().extension(); // cjs and cts files can only use CommonJs modules - if file_ext.is_some_and(|file_ext| matches!(file_ext.as_encoded_bytes(), b"cjs" | b"cts")) { + if file_ext.is_some_and(|file_ext| matches!(file_ext.as_bytes(), b"cjs" | b"cts")) { return None; } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_constant_binary_expression.rs b/crates/biome_js_analyze/src/lint/nursery/no_constant_binary_expression.rs new file mode 100644 index 000000000000..296e085d791e --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_constant_binary_expression.rs @@ -0,0 +1,603 @@ +use biome_analyze::{ + context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, +}; +use biome_console::fmt::{Display, Formatter}; +use biome_console::markup; +use biome_js_semantic::{BindingExtensions, SemanticModel}; +use biome_js_syntax::{ + AnyJsArrayElement, AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, + AnyJsTemplateElement, JsAssignmentOperator, JsBinaryExpression, JsBinaryOperator, + JsCallExpression, JsIdentifierExpression, JsLogicalExpression, JsLogicalOperator, + JsReferenceIdentifier, JsUnaryOperator, +}; +use biome_rowan::{declare_node_union, AstNode, AstNodeList, AstSeparatedList, SyntaxResult}; + +use crate::ast_utils::is_constant_condition; +use crate::globals::is_js_language_global; +use crate::services::semantic::Semantic; + +declare_lint_rule! { + /// Disallow expressions where the operation doesn't affect the value + /// + /// Comparisons which will always evaluate to true or false and logical expressions + /// (`||`, `&&`, `??`) which either always short-circuit or never short-circuit are both likely + /// indications of programmer error. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// const value1 = +x == null; + /// ``` + /// + /// ```js,expect_diagnostic + /// const value2 = condition ? x : {} || DEFAULT; + /// ``` + /// + /// ```js,expect_diagnostic + /// const value3 = !foo == null; + /// ``` + /// + /// ```js,expect_diagnostic + /// const value4 = new Boolean(foo) === true; + /// ``` + /// + /// ```js,expect_diagnostic + /// const objIsEmpty = someObj === {}; + /// ``` + /// + /// ```js,expect_diagnostic + /// const arrIsEmpty = someArr === []; + /// ``` + /// + /// ```js,expect_diagnostic + /// const shortCircuit1 = condition1 && false && condition2; + /// ``` + /// + /// ```js,expect_diagnostic + /// const shortCircuit2 = condition1 || true || condition2; + /// ``` + /// + /// ```js,expect_diagnostic + /// const shortCircuit3 = condition1 ?? "non-nullish" ?? condition2; + /// ``` + /// + /// ### Valid + /// + /// ```js + /// const value1 = x == null; + /// ``` + /// + /// ```js + /// const value2 = (condition ? x : {}) || DEFAULT; + /// ``` + /// + /// ```js + /// const value3 = !(foo == null); + /// ``` + /// + /// ```js + /// const value4 = Boolean(foo) === true; + /// ``` + /// + /// ```js + /// const objIsEmpty = Object.keys(someObj).length === 0; + /// ``` + /// + /// ```js + /// const arrIsEmpty = someArr.length === 0; + /// ``` + /// + pub NoConstantBinaryExpression { + version: "next", + name: "noConstantBinaryExpression", + language: "js", + recommended: false, + sources: &[RuleSource::Eslint("no-constant-binary-expression")], + source_kind: RuleSourceKind::SameLogic, + } +} + +declare_node_union! { + pub Query = JsLogicalExpression | JsBinaryExpression +} + +pub enum Property { + Truthiness, + Nullishness, +} + +impl Display for Property { + fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> { + match self { + Self::Truthiness => fmt.write_str("truthiness"), + Self::Nullishness => fmt.write_str("nullishness"), + } + } +} + +pub enum Issue { + /// A binary expression leads to a constant result, so it can be simplified. + /// For example, `[] == true` is always true, while `[] === true` is always false. + ConstantBinaryOperand { + /// An operand in the expression that will always lead to the same result on comparison + /// with `==`, `!=`, `===`, or `!==` operator. + operand: AnyJsExpression, + }, + + /// The left-hand of a logical expression leads to a constant result, so it can be simplified. + /// For example, `{} ?? foo` will always return the left-hand value, while `null ?? foo` will + /// always return the right-hand value. + ConstantShortCircuit { + /// Left-hand operand of the expression. + left: AnyJsExpression, + + /// Right-hand operand of the expression. + right: AnyJsExpression, + + /// Which property the expression will have constantly, truthiness or nullishness. + property: Property, + }, + + /// A binary expression that always compare to a new object, so it can be simplified. + /// For example, the result of `foo === []` is always false. + AlwaysNew { + /// An operand in the expression that will always construct a new object. + operand: AnyJsExpression, + }, + + /// A strict binary expression that always compare two new objects, so it can be simplified. + /// For example, the result of `[] != []` is always true. + BothAlwaysNew, +} + +impl Rule for NoConstantBinaryExpression { + type Query = Semantic; + type State = Issue; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let model = ctx.model(); + match ctx.query() { + Query::JsLogicalExpression(expr) => { + let operator = expr.operator().ok()?; + let left = expr.left().ok()?; + let right = expr.right().ok()?; + + match operator { + JsLogicalOperator::LogicalAnd | JsLogicalOperator::LogicalOr => { + if is_constant_condition(left.clone(), true, model).is_some() { + return Some(Issue::ConstantShortCircuit { + left, + right, + property: Property::Truthiness, + }); + } + } + JsLogicalOperator::NullishCoalescing => { + if let Ok(true) = has_constant_nullishness(model, &left, false) { + return Some(Issue::ConstantShortCircuit { + left, + right, + property: Property::Nullishness, + }); + } + } + } + } + Query::JsBinaryExpression(expr) => { + let operator = expr.operator().ok()?; + let left = expr.left().ok()?; + let right = expr.right().ok()?; + + if let Some(operand) = + find_binary_expression_constant_operand(model, operator, &left, &right) + { + return Some(Issue::ConstantBinaryOperand { + operand: operand.clone(), + }); + } + + if let Some(operand) = + find_binary_expression_constant_operand(model, operator, &right, &left) + { + return Some(Issue::ConstantBinaryOperand { + operand: operand.clone(), + }); + } + + match operator { + JsBinaryOperator::StrictEquality | JsBinaryOperator::StrictInequality => { + if let Ok(true) = is_always_new(model, &left) { + return Some(Issue::AlwaysNew { operand: left }); + } + if let Ok(true) = is_always_new(model, &right) { + return Some(Issue::AlwaysNew { operand: right }); + } + } + JsBinaryOperator::Equality | JsBinaryOperator::Inequality => { + if let (Ok(true), Ok(true)) = + (is_always_new(model, &left), is_always_new(model, &right)) + { + return Some(Issue::BothAlwaysNew); + } + } + _ => {} + } + } + } + + None + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + + Some(match state { + Issue::ConstantBinaryOperand { .. } => RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { "This binary expression leads to a constant result." }, + ), + Issue::ConstantShortCircuit { left, property, .. } => RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { "This logical expression can be simplified." }, + ) + .detail( + left.range(), + markup! { "This operand always evaluates to the same "{property}"." }, + ), + Issue::AlwaysNew { operand } => RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { "Unexpected comparison to newly constructed object." }, + ) + .detail( + operand.range(), + markup! { "This expression always constructs a new object."}, + ), + Issue::BothAlwaysNew => RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { "Unexpected comparison of two newly constructed objects." }, + ), + }) + } +} + +fn find_binary_expression_constant_operand<'a>( + model: &SemanticModel, + operator: JsBinaryOperator, + a: &'a AnyJsExpression, + b: &'a AnyJsExpression, +) -> Option<&'a AnyJsExpression> { + match operator { + JsBinaryOperator::Equality | JsBinaryOperator::Inequality => { + if (is_null_or_undefined(model, a).ok()? + && has_constant_nullishness(model, b, false).ok()?) + || (is_static_boolean(model, a).ok()? + && has_constant_loose_boolean_comparison(model, b).ok()?) + { + return Some(b); + } + } + JsBinaryOperator::StrictEquality | JsBinaryOperator::StrictInequality => { + if (is_null_or_undefined(model, a).ok()? + && has_constant_nullishness(model, b, false).ok()?) + || (is_static_boolean(model, a).ok()? + && has_constant_strict_boolean_comparison(model, b).ok()?) + { + return Some(b); + } + } + _ => {} + } + + None +} + +// Check the expression will always give the same result when compared to a boolean value loosely. +fn has_constant_loose_boolean_comparison( + model: &SemanticModel, + node: &AnyJsExpression, +) -> SyntaxResult { + Ok(match node.clone().omit_parentheses() { + // always truthy + AnyJsExpression::JsObjectExpression(_) + | AnyJsExpression::JsClassExpression(_) + | AnyJsExpression::JsArrowFunctionExpression(_) + | AnyJsExpression::JsFunctionExpression(_) => true, + + // literals never change + AnyJsExpression::AnyJsLiteralExpression(_) => true, + + // can have a custom .valueOf() or .toString() implementation + AnyJsExpression::JsNewExpression(_) => false, + + AnyJsExpression::JsArrayExpression(expr) => { + // always falsy when the array is empty + expr.elements().is_empty() + // single value array `[x]` can be `[0]` (falsy) or `[1]` (truthy) + || expr.elements().elements().filter(|element| { + matches!(element.node(), Ok(AnyJsArrayElement::AnyJsExpression(_))) + }).count() > 1 + } + + AnyJsExpression::JsUnaryExpression(expr) => match expr.operator()? { + JsUnaryOperator::Void => true, // always undefined + JsUnaryOperator::Typeof => true, // always truthy + JsUnaryOperator::LogicalNot => { + is_constant_condition(expr.argument()?, true, model).is_some() + } + _ => false, + }, + + AnyJsExpression::JsCallExpression(expr) => is_constant_boolean_cast(model, &expr)?, + + AnyJsExpression::JsIdentifierExpression(expr) => is_undefined(model, &expr)?, + + AnyJsExpression::JsTemplateExpression(expr) => !expr + .elements() + .iter() + .any(|element| matches!(element, AnyJsTemplateElement::JsTemplateElement(_))), + + AnyJsExpression::JsAssignmentExpression(expr) => match expr.operator()? { + JsAssignmentOperator::Assign => { + has_constant_loose_boolean_comparison(model, &expr.right()?)? + } + _ => false, + }, + + AnyJsExpression::JsSequenceExpression(expr) => { + has_constant_loose_boolean_comparison(model, &expr.right()?)? + } + + _ => false, + }) +} + +// Check the expression will always give the same result when compared to a boolean value strictly. +fn has_constant_strict_boolean_comparison( + model: &SemanticModel, + node: &AnyJsExpression, +) -> SyntaxResult { + Ok(match node.clone().omit_parentheses() { + // not a boolean + AnyJsExpression::JsObjectExpression(_) + | AnyJsExpression::JsArrayExpression(_) + | AnyJsExpression::JsArrowFunctionExpression(_) + | AnyJsExpression::JsFunctionExpression(_) + | AnyJsExpression::JsClassExpression(_) + | AnyJsExpression::JsNewExpression(_) + | AnyJsExpression::JsTemplateExpression(_) + | AnyJsExpression::JsPreUpdateExpression(_) + | AnyJsExpression::JsPostUpdateExpression(_) => true, + + // literals never change + AnyJsExpression::AnyJsLiteralExpression(_) => true, + + AnyJsExpression::JsBinaryExpression(expr) => matches!( + expr.operator()?, + JsBinaryOperator::Plus + | JsBinaryOperator::Minus + | JsBinaryOperator::Times + | JsBinaryOperator::Divide + | JsBinaryOperator::Remainder + | JsBinaryOperator::Exponent + | JsBinaryOperator::BitwiseAnd + | JsBinaryOperator::BitwiseOr + | JsBinaryOperator::BitwiseXor + | JsBinaryOperator::LeftShift + | JsBinaryOperator::RightShift + | JsBinaryOperator::UnsignedRightShift + ), + + AnyJsExpression::JsUnaryExpression(expr) => match expr.operator()? { + JsUnaryOperator::Delete => false, + JsUnaryOperator::LogicalNot => { + is_constant_condition(expr.argument()?, true, model).is_some() + } + _ => true, + }, + + AnyJsExpression::JsSequenceExpression(expr) => { + has_constant_strict_boolean_comparison(model, &expr.right()?)? + } + + AnyJsExpression::JsIdentifierExpression(expr) => is_undefined(model, &expr)?, + + AnyJsExpression::JsAssignmentExpression(expr) => match expr.operator()? { + JsAssignmentOperator::Assign => { + has_constant_strict_boolean_comparison(model, &expr.right()?)? + } + + // TODO: Handle short-circuiting assignment operators + JsAssignmentOperator::LogicalAndAssign + | JsAssignmentOperator::LogicalOrAssign + | JsAssignmentOperator::NullishCoalescingAssign => false, + + _ => true, + }, + + AnyJsExpression::JsCallExpression(expr) => match expr.callee()? { + AnyJsExpression::JsIdentifierExpression(ident) => { + let name = ident.name()?; + + ((name.has_name("String") || name.has_name("Number")) + && is_global_reference(model, &name)) + || is_constant_boolean_cast(model, &expr)? + } + _ => false, + }, + + _ => false, + }) +} + +/// Check the expression will always result a new object. +fn is_always_new(model: &SemanticModel, node: &AnyJsExpression) -> SyntaxResult { + Ok(match node.clone().omit_parentheses() { + AnyJsExpression::JsObjectExpression(_) + | AnyJsExpression::JsArrayExpression(_) + | AnyJsExpression::JsArrowFunctionExpression(_) + | AnyJsExpression::JsFunctionExpression(_) + | AnyJsExpression::JsClassExpression(_) => true, + + AnyJsExpression::JsNewExpression(expr) => match expr.callee()? { + AnyJsExpression::JsIdentifierExpression(ident) => { + let name = ident.name()?; + is_js_language_global(name.name()?.text()) && is_global_reference(model, &name) + } + _ => false, + }, + + // Regex literals are objects + AnyJsExpression::AnyJsLiteralExpression(expr) => { + matches!(expr, AnyJsLiteralExpression::JsRegexLiteralExpression(_)) + } + + AnyJsExpression::JsConditionalExpression(expr) => { + is_always_new(model, &expr.consequent()?)? && is_always_new(model, &expr.alternate()?)? + } + + _ => false, + }) +} + +/// Check the expression will result always true or always false. +fn is_static_boolean(model: &SemanticModel, node: &AnyJsExpression) -> SyntaxResult { + Ok(match node.clone().omit_parentheses() { + AnyJsExpression::AnyJsLiteralExpression(expr) => { + matches!(expr, AnyJsLiteralExpression::JsBooleanLiteralExpression(_)) + } + + AnyJsExpression::JsCallExpression(expr) => is_constant_boolean_cast(model, &expr)?, + + AnyJsExpression::JsUnaryExpression(expr) => { + expr.operator()? == JsUnaryOperator::LogicalNot + && is_constant_condition(expr.argument()?, true, model).is_some() + } + + _ => false, + }) +} + +/// Check the call expression is `Boolean(x)` call and `x` is a constant condition. +fn is_constant_boolean_cast(model: &SemanticModel, node: &JsCallExpression) -> SyntaxResult { + let AnyJsExpression::JsIdentifierExpression(ident) = node.callee()? else { + return Ok(false); + }; + + let name = ident.name()?; + + Ok( + // Boolean(expr) is constant where expr is constant + name.has_name("Boolean") && is_global_reference(model, &name) && { + match node.arguments()?.args().first() { + Some(arg) => match arg? { + AnyJsCallArgument::AnyJsExpression(expr) => { + is_constant_condition(expr, true, model).is_some() + } + _ => false, + }, + _ => true, + } + }, + ) +} + +/// Check the expression has constant nullishness: always nullish or never nullish. +fn has_constant_nullishness( + model: &SemanticModel, + node: &AnyJsExpression, + non_nullish: bool, +) -> SyntaxResult { + if non_nullish && is_null_or_undefined(model, node)? { + return Ok(false); + } + + Ok(match node.clone().omit_parentheses() { + // never nullish + AnyJsExpression::JsObjectExpression(_) + | AnyJsExpression::JsArrayExpression(_) + | AnyJsExpression::JsArrowFunctionExpression(_) + | AnyJsExpression::JsFunctionExpression(_) + | AnyJsExpression::JsClassExpression(_) + | AnyJsExpression::JsNewExpression(_) + | AnyJsExpression::AnyJsLiteralExpression(_) + | AnyJsExpression::JsTemplateExpression(_) + | AnyJsExpression::JsPreUpdateExpression(_) + | AnyJsExpression::JsPostUpdateExpression(_) + | AnyJsExpression::JsBinaryExpression(_) + | AnyJsExpression::JsUnaryExpression(_) + | AnyJsExpression::JsInstanceofExpression(_) + | AnyJsExpression::JsInExpression(_) => true, + + AnyJsExpression::JsCallExpression(expr) => match expr.callee()? { + AnyJsExpression::JsIdentifierExpression(ident) => { + let name = ident.name()?; + + (name.has_name("Boolean") || name.has_name("String") || name.has_name("Number")) + && model.scope(ident.syntax()).is_global_scope() + } + _ => false, + }, + + AnyJsExpression::JsLogicalExpression(expr) => { + expr.operator()? == JsLogicalOperator::NullishCoalescing + && has_constant_nullishness(model, &expr.right()?, true)? + } + + AnyJsExpression::JsAssignmentExpression(expr) => match expr.operator()? { + JsAssignmentOperator::Assign => { + has_constant_nullishness(model, &expr.right()?, non_nullish)? + } + + // TODO: Handle short-circuiting assignment operators + JsAssignmentOperator::LogicalAndAssign + | JsAssignmentOperator::LogicalOrAssign + | JsAssignmentOperator::NullishCoalescingAssign => false, + + // always non-nullish + _ => true, + }, + + AnyJsExpression::JsSequenceExpression(expr) => { + has_constant_nullishness(model, &expr.right()?, non_nullish)? + } + + AnyJsExpression::JsIdentifierExpression(expr) => is_undefined(model, &expr)?, + + _ => false, + }) +} + +/// Check the expression always result a null or undefined value. +fn is_null_or_undefined(model: &SemanticModel, node: &AnyJsExpression) -> SyntaxResult { + Ok(match node.clone().omit_parentheses() { + AnyJsExpression::AnyJsLiteralExpression(expr) => { + matches!(expr, AnyJsLiteralExpression::JsNullLiteralExpression(_)) + } + + AnyJsExpression::JsIdentifierExpression(expr) => is_undefined(model, &expr)?, + + AnyJsExpression::JsUnaryExpression(expr) => expr.operator()? == JsUnaryOperator::Void, + + _ => false, + }) +} + +/// Check the identifier is a reference of the global `undefined` variable. +fn is_undefined(model: &SemanticModel, node: &JsIdentifierExpression) -> SyntaxResult { + node.name() + .map(|ident| ident.is_undefined() && is_global_reference(model, &ident)) +} + +/// Check the referenced variable is in the global scope. +fn is_global_reference(model: &SemanticModel, node: &JsReferenceIdentifier) -> bool { + node.binding(model) + .map_or(true, |b| b.scope().is_global_scope()) +} diff --git a/crates/biome_js_analyze/src/lint/nursery/no_document_cookie.rs b/crates/biome_js_analyze/src/lint/nursery/no_document_cookie.rs index ef5d5dcbf866..d30fd0f20b47 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_document_cookie.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_document_cookie.rs @@ -99,7 +99,7 @@ fn is_cookie(assignment: &AnyJsAssignment) -> Option<()> { AnyJsAssignment::JsStaticMemberAssignment(static_assignment) => { let property = static_assignment.member().ok()?; - if property.text() != COOKIE { + if property.to_trimmed_string() != COOKIE { return None; }; } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_document_import_in_page.rs b/crates/biome_js_analyze/src/lint/nursery/no_document_import_in_page.rs index 246f0766b877..7cf0ac1a0771 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_document_import_in_page.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_document_import_in_page.rs @@ -1,5 +1,6 @@ use biome_analyze::{ - context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, + RuleSourceKind, }; use biome_console::markup; use biome_js_syntax::{JsFileSource, JsImport}; @@ -35,7 +36,8 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintNext("no-document-import-in-page")], source_kind: RuleSourceKind::SameLogic, - recommended: false, + recommended: true, + domains: &[RuleDomain::Next], } } @@ -68,8 +70,8 @@ impl Rule for NoDocumentImportInPage { return None; } - let file_name = path.file_stem()?.to_str()?; - let parent_name = path.parent()?.file_stem()?.to_str()?; + let file_name = path.file_stem()?; + let parent_name = path.parent()?.file_stem()?; if parent_name == "_document" || file_name == "_document" { return None; @@ -79,7 +81,7 @@ impl Rule for NoDocumentImportInPage { } fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { - return Some( + Some( RuleDiagnostic::new( rule_category!(), ctx.query().range(), @@ -90,6 +92,6 @@ impl Rule for NoDocumentImportInPage { .note(markup! { "Only import ""next/document"" within ""pages/_document.jsx"" to customize the global document structure." }) - ); + ) } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_duplicate_else_if.rs b/crates/biome_js_analyze/src/lint/nursery/no_duplicate_else_if.rs index 6daf7421b23c..396f4b8794e4 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_duplicate_else_if.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_duplicate_else_if.rs @@ -2,6 +2,7 @@ use biome_analyze::{ context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; +use biome_diagnostics::Severity; use biome_js_syntax::{AnyJsExpression, JsIfStatement, JsLogicalOperator}; use biome_rowan::{AstNode, SyntaxNodeCast, TextRange}; @@ -51,6 +52,7 @@ declare_lint_rule! { name: "noDuplicateElseIf", language: "js", recommended: true, + severity: Severity::Error, sources: &[RuleSource::Eslint("no-dupe-else-if")], } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs b/crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs new file mode 100644 index 000000000000..6eee5bde46fd --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_floating_promises.rs @@ -0,0 +1,872 @@ +use biome_analyze::{ + context::RuleContext, declare_lint_rule, FixKind, Rule, RuleDiagnostic, RuleSource, +}; +use biome_console::markup; +use biome_js_factory::make; +use biome_js_semantic::SemanticModel; +use biome_js_syntax::{ + binding_ext::AnyJsBindingDeclaration, global_identifier, AnyJsClassMember, AnyJsExpression, + AnyTsType, JsArrowFunctionExpression, JsCallExpression, JsClassDeclaration, JsClassExpression, + JsClassMemberList, JsExpressionStatement, JsExtendsClause, JsFunctionDeclaration, + JsIdentifierExpression, JsInitializerClause, JsMethodClassMember, JsMethodObjectMember, + JsStaticMemberExpression, JsSyntaxKind, JsThisExpression, JsVariableDeclarator, + TsReturnTypeAnnotation, +}; +use biome_rowan::{ + AstNode, AstNodeList, AstSeparatedList, BatchMutationExt, SyntaxNodeCast, TriviaPieceKind, +}; + +use crate::{services::semantic::Semantic, JsRuleAction}; + +declare_lint_rule! { + /// Require Promise-like statements to be handled appropriately. + /// + /// A "floating" `Promise` is one that is created without any code set up to handle any errors it might throw. + /// Floating Promises can lead to several issues, including improperly sequenced operations, unhandled Promise rejections, and other unintended consequences. + /// + /// This rule will report Promise-valued statements that are not treated in one of the following ways: + /// - Calling its `.then()` method with two arguments + /// - Calling its `.catch()` method with one argument + /// - `await`ing it + /// - `return`ing it + /// - `void`ing it + /// + /// :::caution + /// ## Important notes + /// + /// This rule is a work in progress, and is only partially implemented. + /// Progress is being tracked in the following GitHub issue: https://github.com/biomejs/biome/issues/3187 + /// ::: + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```ts,expect_diagnostic + /// async function returnsPromise(): Promise { + /// return 'value'; + /// } + /// returnsPromise().then(() => {}); + /// ``` + /// + /// ```ts,expect_diagnostic + /// const returnsPromise = async (): Promise => { + /// return 'value'; + /// } + /// async function returnsPromiseInAsyncFunction() { + /// returnsPromise().then(() => {}); + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// const promise = new Promise((resolve) => resolve('value')); + /// promise.then(() => { }).finally(() => { }); + /// ``` + /// + /// ```ts,expect_diagnostic + /// Promise.all([p1, p2, p3]) + /// ``` + /// + /// ```ts,expect_diagnostic + /// class Api { + /// async returnsPromise(): Promise { + /// return 'value'; + /// } + /// async someMethod() { + /// this.returnsPromise(); + /// } + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// class Parent { + /// async returnsPromise(): Promise { + /// return 'value'; + /// } + /// } + /// + /// class Child extends Parent { + /// async someMethod() { + /// this.returnsPromise(); + /// } + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// class Api { + /// async returnsPromise(): Promise { + /// return 'value'; + /// } + /// } + /// const api = new Api(); + /// api.returnsPromise().then(() => {}).finally(() => {}); + /// ``` + /// ### Valid + /// + /// ```ts + /// async function returnsPromise(): Promise { + /// return 'value'; + /// } + /// + /// await returnsPromise(); + /// + /// void returnsPromise(); + /// + /// // Calling .then() with two arguments + /// returnsPromise().then( + /// () => {}, + /// () => {}, + /// ); + /// + /// // Calling .catch() with one argument + /// returnsPromise().catch(() => {}); + /// + /// await Promise.all([p1, p2, p3]) + /// + /// class Api { + /// async returnsPromise(): Promise { + /// return 'value'; + /// } + /// async someMethod() { + /// await this.returnsPromise(); + /// } + /// } + /// ``` + /// + pub NoFloatingPromises { + version: "next", + name: "noFloatingPromises", + language: "ts", + recommended: false, + sources: &[RuleSource::EslintTypeScript("no-floating-promises")], + fix_kind: FixKind::Unsafe, + } +} + +impl Rule for NoFloatingPromises { + type Query = Semantic; + type State = (); + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + let model = ctx.model(); + let expression = node.expression().ok()?; + match expression.omit_parentheses() { + AnyJsExpression::JsCallExpression(js_call_expression) => { + let any_js_expression = js_call_expression.callee().ok()?; + + if !is_callee_a_promise(&any_js_expression, model)? { + return None; + } + + if is_handled_promise(&js_call_expression).unwrap_or_default() { + return None; + } + + Some(()) + } + AnyJsExpression::JsIdentifierExpression(js_identifier_expression) => { + if !is_binding_a_promise(&js_identifier_expression, model, None)? { + return None; + } + + Some(()) + } + AnyJsExpression::JsStaticMemberExpression(static_member_expr) => { + if !is_member_expression_callee_a_promise(&static_member_expr, model)? { + return None; + } + Some(()) + } + _ => None, + } + } + + fn diagnostic(ctx: &RuleContext, _state: &Self::State) -> Option { + let node = ctx.query(); + Some( + RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "A \"floating\" Promise was found, meaning it is not properly handled and could lead to ignored errors or unexpected behavior." + }, + ) + .note(markup! { + "This happens when a Promise is not awaited, lacks a `.catch` or `.then` rejection handler, or is not explicitly ignored using the `void` operator." + }) + ) + } + + fn action(ctx: &RuleContext, _: &Self::State) -> Option { + let node = ctx.query(); + + if !is_in_async_function(node) { + return None; + } + + let expression = node.expression().ok()?; + let mut mutation = ctx.root().begin(); + let await_expression = AnyJsExpression::JsAwaitExpression(make::js_await_expression( + make::token(JsSyntaxKind::AWAIT_KW) + .with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + expression.clone().trim_leading_trivia()?, + )); + + mutation.replace_node(expression, await_expression); + Some(JsRuleAction::new( + ctx.metadata().action_category(ctx.category(), ctx.group()), + ctx.metadata().applicability(), + markup! { "Add await operator." }.to_owned(), + mutation, + )) + } +} + +/// Checks if the callee of a JavaScript expression is a promise. +/// +/// This function inspects the callee of a given JavaScript expression to determine +/// if it is a promise. It returns `true` if the callee is a promise, otherwise `false`. +/// +/// The function works by finding the binding of the callee and checking if it is a promise. +/// +/// # Arguments +/// +/// * `callee` - A reference to an `AnyJsExpression` representing the callee to check. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// +/// # Returns +/// +/// * `true` if the callee is a promise. +/// * `false` otherwise. +/// +/// # Examples +/// +/// Example JavaScript code that would return `true`: +/// ```typescript +/// async function returnsPromise(): Promise { +/// return "value"; +/// } +/// +/// returnsPromise().then(() => {}); +/// ``` +/// +/// Example JavaScript code that would return `false`: +/// ```typescript +/// function doesNotReturnPromise() { +/// return 42; +/// } +/// +/// doesNotReturnPromise().then(() => {}); +/// ``` +fn is_callee_a_promise(callee: &AnyJsExpression, model: &SemanticModel) -> Option { + match callee { + AnyJsExpression::JsIdentifierExpression(js_ident_expr) => { + is_binding_a_promise(js_ident_expr, model, None) + } + AnyJsExpression::JsStaticMemberExpression(static_member_expr) => { + is_member_expression_callee_a_promise(static_member_expr, model) + } + _ => Some(false), + } +} + +/// Checks if a binding is a promise. +/// +/// This function inspects the binding of a given `JsIdentifierExpression` to determine +/// if it is a promise. It returns `Some(true)` if the binding is a promise, `Some(false)` if it is not, +/// and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `js_ident_expr` - A reference to a `JsIdentifierExpression` representing the identifier to check. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// * `target_method_name` - An optional name of the method to check if it is a promise. +/// +/// # Returns +/// +/// * `Some(true)` if the binding is a promise. +/// * `Some(false)` if the binding is not a promise. +/// * `None` if there is an error in the process. +/// +fn is_binding_a_promise( + js_ident_expr: &JsIdentifierExpression, + model: &SemanticModel, + target_method_name: Option<&str>, +) -> Option { + let reference = js_ident_expr.name().ok()?; + let binding = model.binding(&reference)?; + let any_js_binding_decl = binding.tree().declaration()?; + + match any_js_binding_decl { + AnyJsBindingDeclaration::JsFunctionDeclaration(func_decl) => { + Some(is_function_a_promise(&func_decl)) + } + AnyJsBindingDeclaration::JsVariableDeclarator(js_var_decl) => Some( + is_initializer_a_promise(&js_var_decl.initializer()?, model, target_method_name) + .unwrap_or_default() + || is_variable_annotation_a_promise(&js_var_decl).unwrap_or_default(), + ), + _ => Some(false), + } +} + +fn is_function_a_promise(func_decl: &JsFunctionDeclaration) -> bool { + func_decl.async_token().is_some() + || is_return_type_a_promise(func_decl.return_type_annotation()).unwrap_or_default() +} + +/// Checks if a TypeScript return type annotation is a `Promise`. +/// +/// This function inspects the return type annotation of a TypeScript function to determine +/// if it is a `Promise`. It returns `Some(true)` if the return type annotation is `Promise`, +/// `Some(false)` if it is not, and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `return_type` - An optional `TsReturnTypeAnnotation` to check. +/// +/// # Returns +/// +/// * `Some(true)` if the return type annotation is `Promise`. +/// * `Some(false)` if the return type annotation is not `Promise`. +/// * `None` if there is an error in the process. +/// +/// # Examples +/// +/// Example TypeScript code that would return `Some(true)`: +/// ```typescript +/// async function returnsPromise(): Promise {} +/// ``` +/// +/// Example TypeScript code that would return `false`: +/// ```typescript +/// function doesNotReturnPromise(): void {} +/// ``` +fn is_return_type_a_promise(return_type: Option) -> Option { + let ts_return_type_anno = return_type?.ty().ok()?; + let any_ts_type = ts_return_type_anno.as_any_ts_type()?; + let reference_type = any_ts_type.as_ts_reference_type()?; + let any_ts_name = reference_type.name().ok()?; + let name = any_ts_name.as_js_reference_identifier()?; + + Some(name.has_name("Promise")) +} + +/// Checks if a `JsCallExpression` is a handled Promise-like expression. +/// - Calling its .then() with two arguments +/// - Calling its .catch() with one argument +/// +/// This function inspects a `JsCallExpression` to determine if it is a handled Promise-like expression. +/// It returns `Some(true)` if the expression is handled, `Some(false)` if it is not, and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `js_call_expression` - A reference to a `JsCallExpression` to check. +/// +/// # Returns +/// +/// * `Some(true)` if the expression is a handled Promise-like expression. +/// * `Some(false)` if the expression is not a handled Promise-like expression. +/// * `None` if there is an error in the process. +/// +/// # Examples +/// +/// Example TypeScript code that would return `Some(true)`: +/// ```typescript +/// const promise: Promise = new Promise((resolve, reject) => resolve('value')); +/// promise.then(() => "aaa", () => null).finally(() => null) +/// +/// const promise: Promise = new Promise((resolve, reject) => resolve('value')); +/// promise.then(() => "aaa").catch(() => null).finally(() => null) +/// ``` +fn is_handled_promise(js_call_expression: &JsCallExpression) -> Option { + let expr = js_call_expression.callee().ok()?; + let static_member_expr = expr.as_js_static_member_expression()?; + let member = static_member_expr.member().ok()?; + let js_name = member.as_js_name()?; + let value_token = js_name.value_token().ok()?; + let name = value_token.text_trimmed(); + + if name == "finally" { + let expr = static_member_expr.object().ok()?; + let callee = expr.as_js_call_expression()?; + return is_handled_promise(callee); + } + if name == "catch" { + let call_args = js_call_expression.arguments().ok()?; + // just checking if there are any arguments, not if it's a function for simplicity + return Some(call_args.args().len() > 0); + } + if name == "then" { + let call_args = js_call_expression.arguments().ok()?; + // just checking arguments have a reject function from length + return Some(call_args.args().len() >= 2); + } + + Some(false) +} + +/// Checks if the callee of a `JsStaticMemberExpression` is a promise expression. +/// +/// This function inspects the callee of a `JsStaticMemberExpression` to determine +/// if it is a promise expression. It returns `Some(true)` if the callee is a promise expression, +/// `Some(false)` if it is not, and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `static_member_expr` - A reference to a `JsStaticMemberExpression` to check. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// +/// # Returns +/// +/// * `Some(true)` if the callee is a promise expression. +/// * `Some(false)` if the callee is not a promise expression. +/// * `None` if there is an error in the process. +/// +/// # Examples +/// +/// Example TypeScript code that would return `true`: +/// ```typescript +/// async function returnsPromise(): Promise {} +/// +/// returnsPromise().then(() => null).catch(() => {}); +/// +/// globalThis.Promise.reject('value').finally(); +/// ``` +/// +/// Example TypeScript code that would return `false`: +/// ```typescript +/// function doesNotReturnPromise(): void {} +/// +/// doesNotReturnPromise().then(() => null).catch(() => {}); +/// ``` +fn is_member_expression_callee_a_promise( + static_member_expr: &JsStaticMemberExpression, + model: &SemanticModel, +) -> Option { + let expr = static_member_expr.object().ok()?; + + if is_expression_a_promise(&expr, model) { + return Some(true); + } + + match expr { + AnyJsExpression::JsCallExpression(js_call_expr) => { + let callee = js_call_expr.callee().ok()?; + is_callee_a_promise(&callee, model) + } + AnyJsExpression::JsIdentifierExpression(js_ident_expr) => { + let value_token = static_member_expr + .member() + .ok() + .and_then(|js_name| js_name.value_token().ok()); + + if let Some(token) = value_token { + return is_binding_a_promise(&js_ident_expr, model, Some(token.text_trimmed())); + } + is_binding_a_promise(&js_ident_expr, model, None) + } + AnyJsExpression::JsThisExpression(js_this_expr) => { + let js_name = static_member_expr.member().ok()?; + let value_token = js_name.value_token().ok()?; + check_this_expression(&js_this_expr, value_token.text_trimmed(), model) + } + AnyJsExpression::JsStaticMemberExpression(static_member_expr) => { + is_member_expression_callee_a_promise(&static_member_expr, model) + } + _ => Some(false), + } +} + +/// Checks if the given `JsExpressionStatement` is within an async function. +/// +/// This function traverses up the syntax tree from the given expression node +/// to find the nearest function and checks if it is an async function. It +/// supports arrow functions, function declarations, class methods, and object +/// methods. +/// +/// # Arguments +/// +/// * `node` - A reference to a `JsExpressionStatement` to check. +/// +/// # Returns +/// +/// * `true` if the expression is within an async function. +/// * `false` otherwise. +fn is_in_async_function(node: &JsExpressionStatement) -> bool { + node.syntax() + .ancestors() + .find_map(|ancestor| match ancestor.kind() { + JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION => ancestor + .cast::() + .and_then(|func| func.async_token()), + JsSyntaxKind::JS_FUNCTION_DECLARATION => ancestor + .cast::() + .and_then(|func| func.async_token()), + JsSyntaxKind::JS_METHOD_CLASS_MEMBER => ancestor + .cast::() + .and_then(|method| method.async_token()), + JsSyntaxKind::JS_METHOD_OBJECT_MEMBER => ancestor + .cast::() + .and_then(|method| method.async_token()), + _ => None, + }) + .is_some() +} + +/// Checks if the initializer is an async function or returns a promise. +/// +/// This function inspects the initializer of a given `JsVariableDeclarator` to determine +/// if it is an async function or returns a promise. It returns `Some(true)` if the initializer +/// is an async function or returns a promise, `Some(false)` if it is not, and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `js_variable_declarator` - A reference to a `JsVariableDeclarator` to check. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// * `target_method_name` - An optional name of the method to check if it is a promise. +/// +/// # Returns +/// +/// * `Some(true)` if the initializer is an async function or returns a promise. +/// * `Some(false)` if the initializer is not an async function and does not return a promise. +/// * `None` if there is an error in the process. +/// +/// # Examples +/// +/// Example TypeScript code that would return `Some(true)`: +/// +/// ```typescript +/// const returnsPromise = async (): Promise => { +/// return 'value'; +/// } +/// +/// const returnsPromise = async function (): Promise { +/// return 'value' +/// } +/// +/// const promise = new Promise((resolve) => resolve('value')); +/// +/// const promiseWithGlobalIdentifier = new window.Promise((resolve, reject) => resolve('value')); +/// ``` +fn is_initializer_a_promise( + initializer_clause: &JsInitializerClause, + model: &SemanticModel, + target_method_name: Option<&str>, +) -> Option { + let expr = initializer_clause.expression().ok()?; + match expr.omit_parentheses() { + AnyJsExpression::JsArrowFunctionExpression(arrow_func) => Some( + arrow_func.async_token().is_some() + || is_return_type_a_promise(arrow_func.return_type_annotation()) + .unwrap_or_default(), + ), + AnyJsExpression::JsFunctionExpression(func_expr) => Some( + func_expr.async_token().is_some() + || is_return_type_a_promise(func_expr.return_type_annotation()).unwrap_or_default(), + ), + AnyJsExpression::JsNewExpression(js_new_epr) => { + let any_js_expr = js_new_epr.callee().ok()?; + if is_expression_a_promise(&any_js_expr, model) { + return Some(true); + } + let ident_expr = any_js_expr.as_js_identifier_expression()?; + let reference = ident_expr.name().ok()?; + let binding = model.binding(&reference)?; + let any_js_binding_decl = binding.tree().declaration()?; + match any_js_binding_decl { + AnyJsBindingDeclaration::JsClassDeclaration(class_decl) => { + find_and_check_class_member(&class_decl.members(), target_method_name?, model) + } + AnyJsBindingDeclaration::JsVariableDeclarator(js_var_decl) => { + let initializer = js_var_decl.initializer()?; + is_initializer_a_promise(&initializer, model, target_method_name) + } + _ => None, + } + } + AnyJsExpression::JsClassExpression(class_expr) => { + find_and_check_class_member(&class_expr.members(), target_method_name?, model) + } + _ => Some(false), + } +} + +/// Checks if a `JsVariableDeclarator` has a TypeScript type annotation of `Promise`. +/// +/// This function inspects the type annotation of a given `JsVariableDeclarator` to determine +/// if it is a `Promise`. It returns `Some(true)` if the type annotation is `Promise`, +/// `Some(false)` if it is not, and `None` if there is an error in the process. +/// +/// # Arguments +/// +/// * `js_variable_declarator` - A reference to a `JsVariableDeclarator` to check. +/// +/// # Returns +/// +/// * `Some(true)` if the type annotation is `Promise`. +/// * `Some(false)` if the type annotation is not `Promise`. +/// * `None` if there is an error in the process. +/// +/// # Examples +/// +/// Example TypeScript code that would return `Some(true)`: +/// ```typescript +/// const returnsPromise: () => Promise = () => { +/// return Promise.resolve("value") +/// } +/// +/// const promise: Promise = new Promise((resolve) => resolve('value')); +/// ``` +fn is_variable_annotation_a_promise(js_variable_declarator: &JsVariableDeclarator) -> Option { + let any_ts_var_anno = js_variable_declarator.variable_annotation()?; + let ts_type_anno = any_ts_var_anno.as_ts_type_annotation()?; + let any_ts_type = ts_type_anno.ty().ok()?; + is_ts_type_a_promise(&any_ts_type) +} + +/// Checks if an expression is a `Promise`. +/// +/// This function inspects a given `AnyJsExpression` to determine if it represents a `Promise`, +/// either as a global identifier (e.g., `window.Promise`) or directly (e.g., `Promise.resolve`). +/// It also checks that the found `Promise` is not a binding. +/// It returns `true` if the expression is a `Promise` and is not a binding, otherwise `false`. +/// +/// # Arguments +/// +/// * `expr` - A reference to an `AnyJsExpression` to check. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// +/// # Returns +/// +/// * `true` if the expression is a `Promise` and is not a binding. +/// * `false` otherwise. +/// +/// # Examples +/// +/// Example TypeScript code that would return `true`: +/// ```typescript +/// window.Promise.resolve(); +/// globalThis.Promise.resolve(); +/// Promise.resolve('value').then(() => { }); +/// Promise.all([p1, p2, p3]); +/// ``` +/// +/// Example TypeScript code that would return `false`: +/// ```typescript +/// const Promise = { resolve(): {} }; +/// Promise.resolve() +/// ``` +fn is_expression_a_promise(expr: &AnyJsExpression, model: &SemanticModel) -> bool { + let (reference, value) = match global_identifier(expr) { + Some(result) => result, + None => return false, + }; + + if value.text() != "Promise" { + return false; + } + + if model.binding(&reference).is_some() { + return false; + } + + true +} + +/// Traverses up the syntax tree to find the class declaration and checks if a method is a promise. +/// +/// This function traverses up the syntax tree from the given `JsThisExpression` to find the nearest +/// class declaration. It then searches for a method or property in the class that matches the provided +/// `target_name`. If a matching member is found, it checks if the member is a promise. +/// +/// # Arguments +/// +/// * `js_this_expression` - A `JsThisExpression` representing the `this` keyword in the syntax tree. +/// * `target_name` - The name of the method or property to search for. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// +/// # Returns +/// +/// * `Some(true)` if the class member is a promise. +/// * `Some(false)` if the class member is not a promise. +/// * `None` if there is an error in the process or if the class member is not found. +/// +/// # Examples +/// +/// Example TypeScript code that would return `Some(true)`: +/// ```typescript +/// class Api { +/// async returnsPromise(): Promise { +/// return 'value'; +/// } +/// async someMethod() { +/// this.returnsPromise(); +/// } +/// } +/// ``` +/// +/// Example TypeScript code that would return `Some(false)`: +/// ```typescript +/// class Api { +/// returnsString(){ +/// return 'value'; +/// } +/// async someMethod() { +/// this.returnsString(); +/// } +/// } +/// ``` +fn check_this_expression( + js_this_expression: &JsThisExpression, + target_name: &str, + model: &SemanticModel, +) -> Option { + js_this_expression + .syntax() + .ancestors() + .skip(1) + .find_map(|ancestor| { + if ancestor.kind() == JsSyntaxKind::JS_CLASS_MEMBER_LIST { + let class_member_list = JsClassMemberList::cast(ancestor)?; + return find_and_check_class_member(&class_member_list, target_name, model); + } + None + }) +} + +/// Finds a class method or property by matching the given name and checks if it is a promise. +/// +/// This function searches for a class method or property in the given `JsClassMemberList` +/// by matching the provided `target_name`. If a matching member is found, it checks if the member +/// is a promise. If no matching member is found, it checks the parent class (if any) and recursively +/// checks the method or property in the parent class. +/// +/// # Arguments +/// +/// * `class_member_list` - A reference to a `JsClassMemberList` representing the class members to search in. +/// * `target_name` - The name of the method or property to search for. +/// * `model` - A reference to the `SemanticModel` used for resolving bindings. +/// +/// # Returns +/// +/// * `Some(true)` if the class member is a promise. +/// * `Some(false)` if the class member is not a promise. +/// * `None` if there is an error in the process or if the class member is not found. +/// +fn find_and_check_class_member( + class_member_list: &JsClassMemberList, + target_name: &str, + model: &SemanticModel, +) -> Option { + // Check current class first + if let Some(member) = find_class_method_or_property(class_member_list, target_name) { + return is_class_member_a_promise(&member, model); + } + + // Check parent class if exists + check_parent_class(class_member_list, target_name, model) +} + +fn check_parent_class( + class_member_list: &JsClassMemberList, + target_name: &str, + model: &SemanticModel, +) -> Option { + let parent_class_decl = + if let Some(class_decl) = class_member_list.parent::() { + get_parent_class_declaration(&class_decl.extends_clause()?, model)? + } else if let Some(class_expr) = class_member_list.parent::() { + get_parent_class_declaration(&class_expr.extends_clause()?, model)? + } else { + return None; + }; + + find_and_check_class_member(&parent_class_decl.members(), target_name, model) +} + +/// Extracts the parent class declaration from an extends clause +fn get_parent_class_declaration( + extends_clause: &JsExtendsClause, + model: &SemanticModel, +) -> Option { + let super_class = extends_clause.super_class().ok()?; + let identifier_expression = super_class.as_js_identifier_expression()?; + let reference = identifier_expression.name().ok()?; + let binding = model.binding(&reference)?; + let any_js_binding_decl = binding.tree().declaration()?; + + match any_js_binding_decl { + AnyJsBindingDeclaration::JsClassDeclaration(parent_class_decl) => Some(parent_class_decl), + _ => None, + } +} + +fn find_class_method_or_property( + class_member_list: &JsClassMemberList, + target_name: &str, +) -> Option { + class_member_list.iter().find(|member| match member { + AnyJsClassMember::JsMethodClassMember(method) => method + .name() + .ok() + .and_then(|name| name.name()) + .is_some_and(|class_member_name| class_member_name.text() == target_name), + AnyJsClassMember::JsPropertyClassMember(property) => property + .name() + .ok() + .and_then(|name| name.name()) + .is_some_and(|class_member_name| class_member_name.text() == target_name), + _ => false, + }) +} + +fn is_class_member_a_promise( + class_member: &AnyJsClassMember, + model: &SemanticModel, +) -> Option { + match class_member { + AnyJsClassMember::JsMethodClassMember(method) => Some( + method.async_token().is_some() + || is_return_type_a_promise(method.return_type_annotation()).unwrap_or_default(), + ), + AnyJsClassMember::JsPropertyClassMember(property) => { + if let Some(property_annotation) = property.property_annotation() { + let ts_type_annotation = property_annotation.as_ts_type_annotation()?; + let any_ts_type = ts_type_annotation.ty().ok()?; + + return is_ts_type_a_promise(&any_ts_type); + } + + if let Some(initializer_clause) = property.value() { + return is_initializer_a_promise(&initializer_clause, model, None); + } + + None + } + _ => None, + } +} + +fn is_ts_type_a_promise(any_ts_type: &AnyTsType) -> Option { + match any_ts_type { + AnyTsType::TsFunctionType(func_type) => { + let return_type = func_type.return_type().ok()?; + let ref_type = return_type.as_any_ts_type()?.as_ts_reference_type()?; + let name = ref_type.name().ok()?; + let identifier = name.as_js_reference_identifier()?; + + Some(identifier.has_name("Promise")) + } + AnyTsType::TsReferenceType(ts_ref_type) => { + let name = ts_ref_type.name().ok()?; + let identifier = name.as_js_reference_identifier()?; + + Some(identifier.has_name("Promise")) + } + _ => None, + } +} diff --git a/crates/biome_js_analyze/src/lint/nursery/no_head_element.rs b/crates/biome_js_analyze/src/lint/nursery/no_head_element.rs index b03b88e8448b..756389de5f29 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_head_element.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_head_element.rs @@ -1,6 +1,6 @@ use biome_analyze::RuleSourceKind; use biome_analyze::{ - context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, }; use biome_console::markup; use biome_js_syntax::JsxOpeningElement; @@ -49,7 +49,8 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintNext("no-head-element")], source_kind: RuleSourceKind::SameLogic, - recommended: false, + recommended: true, + domains: &[RuleDomain::Next], } } @@ -67,10 +68,10 @@ impl Rule for NoHeadElement { let is_in_app_dir = ctx .file_path() .ancestors() - .any(|a| a.file_name().map_or(false, |f| f == "app" && a.is_dir())); + .any(|a| a.file_name().is_some_and(|f| f == "app" && a.is_dir())); if !is_in_app_dir { - return Some(element.syntax().text_range()); + return Some(element.syntax().text_range_with_trivia()); } } @@ -78,12 +79,12 @@ impl Rule for NoHeadElement { } fn diagnostic(_: &RuleContext, range: &Self::State) -> Option { - return Some(RuleDiagnostic::new( + Some(RuleDiagnostic::new( rule_category!(), range, markup! { "Don't use """" element." }, ).note(markup! { "Using the """" element can cause unexpected behavior in a Next.js application. Use """" from ""next/head"" instead." - })); + })) } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_head_import_in_document.rs b/crates/biome_js_analyze/src/lint/nursery/no_head_import_in_document.rs index 27c3f5c70819..86880ba7bf81 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_head_import_in_document.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_head_import_in_document.rs @@ -1,5 +1,6 @@ use biome_analyze::{ - context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, + RuleSourceKind, }; use biome_console::markup; use biome_js_syntax::{JsFileSource, JsImport}; @@ -48,7 +49,8 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintNext("no-head-import-in-document")], source_kind: RuleSourceKind::SameLogic, - recommended: false, + recommended: true, + domains: &[RuleDomain::Next], } } @@ -81,14 +83,14 @@ impl Rule for NoHeadImportInDocument { return None; } - let file_name = path.file_stem()?.to_str()?; + let file_name = path.file_stem()?; // pages/_document.(jsx|tsx) if file_name == "_document" { return Some(()); } - let parent_name = path.parent()?.file_stem()?.to_str()?; + let parent_name = path.parent()?.file_stem()?; // pages/_document/index.(jsx|tsx) if parent_name == "_document" && file_name == "index" { @@ -99,14 +101,14 @@ impl Rule for NoHeadImportInDocument { } fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { - let path = ctx.file_path().to_str()?.split("pages").nth(1)?; + let path = ctx.file_path().as_str().split("pages").nth(1)?; let path = if cfg!(debug_assertions) { path.replace(MAIN_SEPARATOR, "/") } else { path.to_string() }; - return Some( + Some( RuleDiagnostic::new( rule_category!(), ctx.query().range(), @@ -117,6 +119,6 @@ impl Rule for NoHeadImportInDocument { .note(markup! { "Using the ""next/head"" in document pages can cause unexpected issues. Use """" from ""next/document"" instead." }) - ); + ) } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_img_element.rs b/crates/biome_js_analyze/src/lint/nursery/no_img_element.rs index 6a7d97ac4757..0d623a244885 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_img_element.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_img_element.rs @@ -1,6 +1,6 @@ -use biome_analyze::RuleSourceKind; use biome_analyze::{ - context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, + RuleSourceKind, }; use biome_console::markup; use biome_js_syntax::jsx_ext::AnyJsxElement; @@ -57,7 +57,8 @@ declare_lint_rule! { language: "jsx", sources: &[RuleSource::EslintNext("no-img-element")], source_kind: RuleSourceKind::SameLogic, - recommended: false, + recommended: true, + domains: &[RuleDomain::Next], } } @@ -97,7 +98,7 @@ impl Rule for NoImgElement { } fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { - return Some( + Some( RuleDiagnostic::new( rule_category!(), ctx.query().range(), @@ -108,6 +109,6 @@ impl Rule for NoImgElement { .note(markup! { "Using the """" can lead to slower LCP and higher bandwidth. Consider using """" from ""next/image"" to automatically optimize images." }) - ); + ) } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_import_cycles.rs b/crates/biome_js_analyze/src/lint/nursery/no_import_cycles.rs new file mode 100644 index 000000000000..8a51a8029478 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_import_cycles.rs @@ -0,0 +1,212 @@ +use std::collections::HashSet; + +use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; +use biome_console::markup; +use biome_dependency_graph::ModuleImports; +use biome_diagnostics::Severity; +use biome_js_syntax::{inner_string_text, AnyJsImportLike}; +use biome_rowan::AstNode; +use camino::{Utf8Path, Utf8PathBuf}; + +use crate::services::dependency_graph::ResolvedImports; + +declare_lint_rule! { + /// Prevent import cycles. + /// + /// This rule warns when a file imports another file that, either directly + /// or indirectly, imports the original file again. + /// + /// Cycles can lead to symbols that are unexpectedly `undefined` and are + /// generally considered poor code hygiene. + /// + /// If a cycle is detected, it is advised to move code such that imports + /// only go in a single direction, i.e. they don't point "back" to the + /// importing file. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// **`foobar.js`** + /// ```js + /// import { baz } from "./baz.js"; + /// + /// export function foo() { + /// baz(); + /// } + /// + /// export function bar() { + /// console.log("foobar"); + /// } + /// ``` + /// + /// **`baz.js`** + /// ```js + /// import { bar } from "./foobar.js"; + /// + /// export function baz() { + /// bar(); + /// } + /// ``` + /// + /// ### Valid + /// + /// **`foo.js`** + /// ```js + /// import { baz } from "./baz.js"; + /// + /// export function foo() { + /// baz(); + /// } + /// ``` + /// + /// **`bar.js`** + /// ```js + /// export function bar() { + /// console.log("foobar"); + /// } + /// ``` + /// + /// **`baz.js`** + /// ```js + /// import { bar } from "./bar.js"; + /// + /// export function baz() { + /// bar(); + /// } + /// ``` + /// + pub NoImportCycles { + version: "next", + name: "noImportCycles", + language: "js", + sources: &[ + RuleSource::EslintImport("no-cycle"), + ], + severity: Severity::Warning, + recommended: false, + } +} + +impl Rule for NoImportCycles { + type Query = ResolvedImports; + type State = Vec; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let file_imports = ctx.imports_for_path(ctx.file_path())?; + + let node = ctx.query(); + let name_token = node.module_name_token()?; + let specifier_text = inner_string_text(&name_token); + let specifier = specifier_text.text(); + let import = if is_static_import(node) { + file_imports.static_imports.get(specifier) + } else { + file_imports.dynamic_imports.get(specifier) + }?; + let resolved_path = import.resolved_path.as_ref().ok()?; + + let imports = ctx.imports_for_path(resolved_path)?; + find_cycle(ctx, resolved_path, imports) + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + + let cwd = Utf8PathBuf::from( + std::env::current_dir() + .map(|cwd| cwd.to_string_lossy().to_string()) + .unwrap_or_default(), + ); + + let mut note = markup!("This import resolves to ").to_owned(); + for (i, path) in state.iter().enumerate() { + if i > 0 { + note.extend_with(markup!("\n ... which imports ")); + } + + match Utf8Path::new(path).strip_prefix(&cwd) { + Ok(relative_path) => { + note.extend_with(markup!({relative_path.as_str()})) + } + Err(_) => note.extend_with(markup!({path})), + } + } + note.extend_with(markup!("\n ... which is the file we're importing from.")); + + Some( + RuleDiagnostic::new( + rule_category!(), + node.range(), + markup!("This import is part of a cycle."), + ) + .note(note), + ) + } +} + +/// Attempts to find a cycle by traversing all imports from `start_path` and +/// finding those that lead back to `ctx.file_path()`. +/// +/// Cycles that don't lead back to `ctx.file_path()` are not reported, since +/// they should be reported for the respective files instead. +/// +/// `imports` are the imports found in `start_path`. +/// +/// If a cycle is found, this returns a vector with all the paths involved in +/// the cycle, starting with `start_path` and ending with `ctx.file_path()`. +fn find_cycle( + ctx: &RuleContext, + start_path: &Utf8Path, + mut imports: ModuleImports, +) -> Option> { + let mut seen = HashSet::new(); + let mut stack = Vec::new(); + + 'outer: loop { + while let Some((_specifier, import)) = imports.drain_one() { + let Ok(resolved_path) = import.resolved_path else { + continue; + }; + + if resolved_path == ctx.file_path() { + // Return all the paths from `start_path` to `resolved_path`: + let paths = Some(start_path.to_string()) + .into_iter() + .chain(stack.into_iter().map(|(path, _)| path)) + .chain(Some(resolved_path.into())) + .collect(); + return Some(paths); + } + + // FIXME: Use `get_or_insert_with()` once it's stabilized. + // See: https://github.com/rust-lang/rust/issues/60896 + if seen.contains(resolved_path.as_str()) { + continue; + } + + seen.insert(resolved_path.to_string()); + + if let Some(resolved_imports) = ctx.imports_for_path(&resolved_path) { + stack.push((resolved_path.into(), imports)); + imports = resolved_imports; + continue 'outer; + } + } + + match stack.pop() { + Some((_previous_path, previous_imports)) => { + imports = previous_imports; + } + None => break, + } + } + + None +} + +fn is_static_import(node: &AnyJsImportLike) -> bool { + matches!(node, AnyJsImportLike::JsModuleSource(_)) +} diff --git a/crates/biome_js_analyze/src/lint/nursery/use_import_restrictions.rs b/crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs similarity index 69% rename from crates/biome_js_analyze/src/lint/nursery/use_import_restrictions.rs rename to crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs index 2734135a7e6e..757316e9969e 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_import_restrictions.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs @@ -1,38 +1,35 @@ use biome_analyze::{ - context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; -use biome_js_syntax::JsModuleSource; -use biome_rowan::{AstNode, TokenText}; +use biome_js_syntax::{inner_string_text, AnyJsImportLike}; +use biome_rowan::{TextRange, TokenText}; const INDEX_BASENAMES: &[&str] = &["index", "mod"]; const SOURCE_EXTENSIONS: &[&str] = &["js", "ts", "cjs", "cts", "mjs", "mts", "jsx", "tsx"]; declare_lint_rule! { - /// Disallows package private imports. + /// Restricts imports of "package private" exports. /// - /// This rules enforces the following restrictions: - /// - /// ## Package private visibility - /// - /// All exported symbols, such as types, functions or other things that may be exported, are - /// considered to be "package private". This means that modules that reside in the same - /// directory, as well as submodules of those "sibling" modules, are allowed to import them, - /// while any other modules that are further away in the file system are restricted from - /// importing them. A symbol's visibility may be extended by re-exporting from an index file. + /// By enabling this rule, all exported symbols, such as types, functions + /// or other things that may be exported, are considered to be "package + /// private". This means that modules that reside in the same directory, as + /// well as submodules of those "sibling" modules, are allowed to import + /// them, while any other modules that are further away in the file system + /// are restricted from importing them. A symbol's visibility may be + /// extended by re-exporting from an index file. /// /// Notes: /// - /// * This rule only applies to relative imports. External dependencies are exempted. - /// * This rule only applies to imports for JavaScript and TypeScript files. Imports for - /// resources such as images or CSS files are exempted. + /// * This rule only applies to relative imports. External dependencies + /// as well as TypeScript aliases are exempted. + /// * This rule only applies to imports for JavaScript and TypeScript + /// files. Imports for resources such as images or CSS files are exempted. /// /// Source: https://github.com/uhyo/eslint-plugin-import-access /// - /// ## Examples - /// - /// ### Invalid + /// #### Examples (Invalid) /// /// ```js,expect_diagnostic /// // Attempt to import from `foo.js` from outside its `sub` module. @@ -53,7 +50,7 @@ declare_lint_rule! { /// import { fooPackageVariable } from "./sub/foo/index.js"; /// ``` /// - /// ### Valid + /// #### Examples (Valid) /// /// ```js /// // Imports within the same module are always allowed. @@ -72,60 +69,66 @@ declare_lint_rule! { /// import useAsync from "react-use/lib/useAsync"; /// ``` /// - pub UseImportRestrictions { - version: "1.0.0", - name: "useImportRestrictions", + pub NoPackagePrivateImports { + version: "next", + name: "noPackagePrivateImports", language: "js", - sources: &[RuleSource::EslintImportAccess("eslint-plugin-import-access")], - source_kind: RuleSourceKind::Inspired, + sources: &[ + RuleSource::EslintImportAccess("eslint-plugin-import-access") + ], recommended: false, } } -impl Rule for UseImportRestrictions { - // TODO. This does not handle dynamic imports and require calls. - type Query = Ast; - type State = ImportRestrictionsState; +pub struct NoPackagePrivateImportsState { + range: TextRange, + + /// The path that is being restricted. + path: String, + + /// Suggestion from which to import instead. + suggestion: String, +} + +impl Rule for NoPackagePrivateImports { + type Query = Ast; + type State = NoPackagePrivateImportsState; type Signals = Option; type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { - let binding = ctx.query(); - let Ok(path) = binding.inner_string_text() else { + let node = ctx.query(); + if node.is_in_ts_module_declaration() { return None; - }; + } - get_restricted_import(&path) - } + let module_name = node.module_name_token()?; + let import_source_text = inner_string_text(&module_name); - fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { - let ImportRestrictionsState { path, suggestion } = state; + get_restricted_import(module_name.text_trimmed_range(), &import_source_text) + } + fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { let diagnostic = RuleDiagnostic::new( rule_category!(), - ctx.query().range(), + state.range, markup! { - "Importing package private symbols is prohibited from outside the module directory." + "Importing package private symbols is disallowed from outside the module directory." }, ) .note(markup! { - "Please import from "{suggestion}" instead " - "(you may need to re-export the symbol(s) from "{path}")." + "Please import from "{state.suggestion}" instead " + "(you may need to re-export the symbol(s) from "{state.path}")." }); Some(diagnostic) } } -pub struct ImportRestrictionsState { - /// The path that is being restricted. - path: String, - - /// Suggestion from which to import instead. - suggestion: String, -} - -fn get_restricted_import(module_path: &TokenText) -> Option { +fn get_restricted_import( + range: TextRange, + module_path: &TokenText, +) -> Option { if !module_path.starts_with('.') { return None; } @@ -172,7 +175,8 @@ fn get_restricted_import(module_path: &TokenText) -> Option for CustomRestrictedImportOptions { impl Deserializable for CustomRestrictedImport { fn deserialize( + ctx: &mut impl DeserializationContext, value: &impl DeserializableValue, name: &str, - diagnostics: &mut Vec, ) -> Option { if value.visitable_type()? == DeserializableType::Str { - biome_deserialize::Deserializable::deserialize(value, name, diagnostics) - .map(Self::Plain) + biome_deserialize::Deserializable::deserialize(ctx, value, name).map(Self::Plain) } else { - biome_deserialize::Deserializable::deserialize(value, name, diagnostics) - .map(Self::WithOptions) + biome_deserialize::Deserializable::deserialize(ctx, value, name).map(Self::WithOptions) } } } @@ -501,7 +500,7 @@ struct RestrictedImportVisitor<'a> { results: Vec, } -impl<'a> RestrictedImportVisitor<'a> { +impl RestrictedImportVisitor<'_> { pub const BARE_IMPORT_ALIAS: &'static str = ""; pub const NAMESPACE_IMPORT_ALIAS: &'static str = "*"; pub const DEFAULT_IMPORT_ALIAS: &'static str = "default"; @@ -587,7 +586,7 @@ impl<'a> RestrictedImportVisitor<'a> { // #2: **(import("")).then**(...) let static_member_expr = current.cast::()?; let member_name = static_member_expr.member().ok()?; - if member_name.as_js_name()?.text() != "then" { + if member_name.as_js_name()?.syntax().text_trimmed() != "then" { return None; } current = static_member_expr.syntax().parent()?; @@ -828,8 +827,7 @@ impl<'a> RestrictedImportVisitor<'a> { /// Checks whether this import of the form `const local_name = import(...)` is allowed. fn visit_namespace_binding(&mut self, namespace_import: &JsIdentifierBinding) -> Option<()> { - return self - .visit_special_import_node(namespace_import.syntax(), Self::NAMESPACE_IMPORT_ALIAS); + self.visit_special_import_node(namespace_import.syntax(), Self::NAMESPACE_IMPORT_ALIAS) } /// Checks whether this import of the form `{ imported_name }` is allowed. @@ -952,10 +950,10 @@ impl<'a> RestrictedImportVisitor<'a> { } pub struct RestrictedImportMessage { - pub location: TextRange, - pub message: String, - pub import_source: String, - pub allowed_import_names: Box<[Box]>, + location: TextRange, + message: String, + import_source: String, + allowed_import_names: Box<[Box]>, } impl Rule for NoRestrictedImports { @@ -967,16 +965,16 @@ impl Rule for NoRestrictedImports { fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); if node.is_in_ts_module_declaration() { - return [].into(); + return Vec::new(); } let Some(module_name) = node.module_name_token() else { - return vec![]; + return Vec::new(); }; let import_source_text = inner_string_text(&module_name); let import_source = import_source_text.text(); let Some(restricted_import_settings) = ctx.options().paths.get(import_source) else { - return vec![]; + return Vec::new(); }; let restricted_import: CustomRestrictedImportOptions = restricted_import_settings.clone().into(); @@ -1059,15 +1057,22 @@ impl Rule for NoRestrictedImports { } fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { + let RestrictedImportMessage { + import_source, + allowed_import_names, + location, + message, + } = state; + let mut rule_diagnostic = RuleDiagnostic::new( rule_category!(), - state.location, + location, markup! { - {state.message} + {message} }, ); - if !state.allowed_import_names.is_empty() { - let mut sorted = state.allowed_import_names.to_vec(); + if !allowed_import_names.is_empty() { + let mut sorted = allowed_import_names.to_vec(); sorted.sort(); let allowed_import_names = sorted.into_iter().map(|name| { if &*name == RestrictedImportVisitor::BARE_IMPORT_ALIAS { @@ -1078,9 +1083,9 @@ impl Rule for NoRestrictedImports { }); rule_diagnostic = rule_diagnostic.footer_list( - markup! { "Only the following imports from ""'"{state.import_source}"'"" are allowed:" }, - allowed_import_names, - ); + markup! { "Only the following imports from ""'"{import_source}"'"" are allowed:" }, + allowed_import_names, + ); } Some(rule_diagnostic) } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_restricted_types.rs b/crates/biome_js_analyze/src/lint/nursery/no_restricted_types.rs index 58f3c243fc90..b08130068e45 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_restricted_types.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_restricted_types.rs @@ -4,7 +4,7 @@ use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; use biome_deserialize::{ - Deserializable, DeserializableType, DeserializableValue, DeserializationDiagnostic, + Deserializable, DeserializableType, DeserializableValue, DeserializationContext, }; use biome_js_factory::make; use biome_js_syntax::TsReferenceType; @@ -166,16 +166,14 @@ impl From for CustomRestrictedTypeOptions { impl Deserializable for CustomRestrictedType { fn deserialize( + ctx: &mut impl DeserializationContext, value: &impl DeserializableValue, name: &str, - diagnostics: &mut Vec, ) -> Option { if value.visitable_type()? == DeserializableType::Str { - biome_deserialize::Deserializable::deserialize(value, name, diagnostics) - .map(Self::Plain) + biome_deserialize::Deserializable::deserialize(ctx, value, name).map(Self::Plain) } else { - biome_deserialize::Deserializable::deserialize(value, name, diagnostics) - .map(Self::WithOptions) + biome_deserialize::Deserializable::deserialize(ctx, value, name).map(Self::WithOptions) } } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs b/crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs index 4d30d51e5b3f..ec9ac4c5abba 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_static_element_interactions.rs @@ -151,13 +151,13 @@ impl Rule for NoStaticElementInteractions { */ fn is_hidden_from_screen_reader(node: &AnyJsxElement, element_name: &str) -> bool { node.find_attribute_by_name("aria-hidden") - .map_or(false, |attr| { + .is_some_and(|attr| { attr.as_static_value() .map_or(true, |val| val.text() == "true") })//
    || (element_name == "input" - && node.find_attribute_by_name("type").map_or(false, |attr| { + && node.find_attribute_by_name("type").is_some_and(|attr| { attr.as_static_value() - .map_or(false, |val| val.text() == "hidden") + .is_some_and(|val| val.text() == "hidden") })) // } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_ts_ignore.rs b/crates/biome_js_analyze/src/lint/nursery/no_ts_ignore.rs new file mode 100644 index 000000000000..bdf89ca63ead --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_ts_ignore.rs @@ -0,0 +1,145 @@ +use crate::JsRuleAction; +use biome_analyze::{ + context::RuleContext, declare_lint_rule, Ast, FixKind, Rule, RuleDiagnostic, RuleSource, + RuleSourceKind, +}; +use biome_console::markup; +use biome_diagnostics::Severity; +use biome_js_syntax::{JsModule, JsSyntaxToken, TextLen}; +use biome_rowan::{AstNode, BatchMutationExt, Direction, TextRange, TextSize, TriviaPiece}; + +declare_lint_rule! { + /// Prevents the use of the TypeScript directive `@ts-ignore`. + /// + /// The directive `@ts-ignore` suppresses all compilation errors, even ones that could be considered bugs + /// coming from an upstream library or the compiler itself. If you use `@ts-ignore`, it won't be possible to know + /// when and if the bug is fixed. + /// + /// The rule promotes the use the directive `@ts-expect-error`, which is meant to raise an error if there aren't any errors. + /// This means that once the bug is fixed, you can delete the directive, safely. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```ts,expect_diagnostic + /// // @ts-ignore + /// let foo; + /// ``` + /// + /// ### Valid + /// + /// ```ts + /// // @ts-expect-error + /// let foo; + /// ``` + /// + pub NoTsIgnore { + version: "next", + name: "noTsIgnore", + language: "js", + sources: &[RuleSource::Eslint("ban-ts-comment")], + recommended: true, + source_kind: RuleSourceKind::Inspired, + fix_kind: FixKind::Safe, + severity: Severity::Warning, + } +} + +/// We track the token that has the trivia, and the range when the incorrect comment is the document +type RuleState = (JsSyntaxToken, TextRange); + +impl Rule for NoTsIgnore { + type Query = Ast; + type State = RuleState; + type Signals = Vec; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let module = ctx.query(); + + let mut tokens = vec![]; + for token in module.syntax().descendants_tokens(Direction::Next) { + let leading_trivia = token.leading_trivia(); + let comments: Vec<_> = leading_trivia + .pieces() + .filter_map(|trivia| { + if let Some(comment) = trivia.as_comments() { + if let Some((index, _)) = comment.text().match_indices("@ts-ignore").next() + { + return Some(( + token.clone(), + comment.text_range().add_start(TextSize::from(index as u32)), + )); + } + } + None + }) + .collect(); + + tokens.extend(comments); + } + + tokens + } + + fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { + let (token, range) = state; + + Some( + RuleDiagnostic::new( + rule_category!(), + range, + markup! { + "Unsafe use of the ""@ts-ignore"" directive found in this comment." + }, + ) + .detail( + token.text_trimmed_range(), + markup! { + "The directive is applied to this line." + }, + ) + .note(markup! { + "The ""@ts-ignore"" directive suppresses any kind of error, even possible errors that might be fixed by upstream libraries or the compiler itself." + }), + ) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + let (token, _) = state; + let token = token.clone(); + let mut mutation = ctx.root().begin(); + let mut new_trivia = vec![]; + let mut text = String::new(); + for trivia in token.clone().leading_trivia().pieces() { + let kind = trivia.kind(); + if let Some(comment) = trivia.as_comments() { + if comment.text().contains("@ts-ignore") { + let new_comment = comment.text().replace("@ts-ignore", "@ts-expect-error"); + new_trivia.push(TriviaPiece::new(kind, new_comment.text_len())); + text.push_str(new_comment.as_str()); + } else { + new_trivia.push(TriviaPiece::new(kind, comment.text_len())); + text.push_str(comment.text()); + } + } else { + new_trivia.push(TriviaPiece::new(kind, trivia.text_len())); + text.push_str(trivia.text()); + } + } + text.push_str(token.text_trimmed()); + let new_token = JsSyntaxToken::new_detached(token.kind(), text.as_str(), new_trivia, []) + .with_trailing_trivia_pieces(token.trailing_trivia().pieces()); + + mutation.replace_token_discard_trivia(token, new_token); + + Some(JsRuleAction::new( + ctx.metadata().action_category(ctx.category(), ctx.group()), + ctx.metadata().applicability(), + markup! { "Use the ""@ts-expect-error"" directive instead." } + .to_owned(), + mutation, + )) + } +} diff --git a/crates/biome_js_analyze/src/lint/nursery/no_unwanted_polyfillio.rs b/crates/biome_js_analyze/src/lint/nursery/no_unwanted_polyfillio.rs new file mode 100644 index 000000000000..0670e1ac4742 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_unwanted_polyfillio.rs @@ -0,0 +1,277 @@ +use std::sync::LazyLock; + +use biome_analyze::{ + context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleDomain, RuleSource, + RuleSourceKind, +}; +use biome_console::markup; +use biome_diagnostics::Severity; +use biome_js_syntax::jsx_ext::AnyJsxElement; +use biome_rowan::{AstNode, TextRange}; +use regex::Regex; + +use crate::{ + nextjs::{is_next_import, NextUtility}, + services::semantic::Semantic, +}; + +declare_lint_rule! { + /// Prevent duplicate polyfills from Polyfill.io. + /// + /// You are using polyfills from Polyfill.io and including polyfills already shipped with Next.js. + /// This unnecessarily increases page weight which can affect loading performance. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```jsx,expect_diagnostic + /// + /// ``` + /// + /// ```jsx,expect_diagnostic + /// import NextScript from 'next/script'; + /// + /// export function MyApp({ Component, pageProps }) { + /// return + /// } + /// ``` + /// + /// ### Valid + /// + /// ```jsx + /// <> + /// + /// + /// + + + +
    + ); + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/invalid.jsx.snap new file mode 100644 index 000000000000..25cb28d67d2b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/invalid.jsx.snap @@ -0,0 +1,100 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.jsx +--- +# Input +```jsx +import {Head} from 'next/document'; + +export class Blah extends Head { + render() { + return ( +
    +

    Hello title

    + + + + +
    + ); + } +} + +``` + +# Diagnostics +``` +invalid.jsx:8:23 lint/nursery/noUnwantedPolyfillio ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prevent duplicate polyfills from Polyfill.io + + 6 │
    + 7 │

    Hello title

    + > 8 │ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ + 10 │ + + i WeakSet, Promise, Promise.prototype.finally, es2015, es5, es6 are already shipped with Next.js. + + i This unnecessarily increases page weight which can affect loading performance. + + +``` + +``` +invalid.jsx:9:23 lint/nursery/noUnwantedPolyfillio ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prevent duplicate polyfills from Polyfill.io + + 7 │

    Hello title

    + 8 │ + > 9 │ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 │ + 11 │ + + i Array.prototype.copyWithin is already shipped with Next.js. + + i This unnecessarily increases page weight which can affect loading performance. + + +``` + +``` +invalid.jsx:10:23 lint/nursery/noUnwantedPolyfillio ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prevent duplicate polyfills from Polyfill.io + + 8 │ + 9 │ + > 10 │ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 11 │ + 12 │
    + + i Object.fromEntries is already shipped with Next.js. + + i This unnecessarily increases page weight which can affect loading performance. + + +``` + +``` +invalid.jsx:11:23 lint/nursery/noUnwantedPolyfillio ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prevent duplicate polyfills from Polyfill.io + + 9 │ + 10 │ + > 11 │ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 12 │
    + 13 │ ); + + i Object.fromEntries is already shipped with Next.js. + + i This unnecessarily increases page weight which can affect loading performance. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/valid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/valid.jsx new file mode 100644 index 000000000000..27e1c335266e --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noUnwantedPolyfillio/valid.jsx @@ -0,0 +1,13 @@ +import Script from 'next/script'; + +export function MyApp({ Component, pageProps }) { + return ( +
    + + + + + +