Repository: prettier/prettier (JavaScript)

Issue:
Change HTML/JSX formatting to have one attribute/prop per line
Similar to what was done for #3847 I think it best to break some of the discussion from #3101 into a new issue that people can 👍 or 👎.

I'm proposing that for HTML and JSX to have Prettier always have one attribute/prop per line (and thus not respect the developer's original preference). Code would then be formatted as shown below. This is in contrast to the current behaviour where we fit as much as possible with the print-width.

**Expected behavior:**
```jsx
<MyComponent lorem="1"/>

<MyComponent
  lorem="1"
  ipsum="2"
/>

<MyComponent
  lorem="1"
  ipsum="2"
  dolor="3"
/>
```

This suggestion for one attribute/prop per line was proposed several times in the discussion of #3101 but I think it is clearer if this is pulled into it's own proposal. The original proposal in #3101 is that Prettier would add an option to preserve original formatting which, while I agree with the author with the style that they want, I don't think a) an option, nor b) preserving original formatting follows the aims for Prettier (see also #2068). Instead I think the aims of #3101 are better served by this proposal to ignore the print-width and always place attributes/props on new lines (assuming that there is more than one).

This style appears to be the most common formatting for Angular, Vue and React from what I can tell. It style appears to be the style enforced by the rules:
* React: [jsx-max-props-per-line](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md) which [Airbnb set to to 1 in their style guide](https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb/rules/react.js#L89)
* Vue: [max-attributes-per-line](https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/max-attributes-per-line.md) which has a default to 1.


Hints:
While I agree that I'd like to see this being default behavior, I would suggest the following steps:

- introduce new config option (`singleAttributePerLine: true` ?)
- make default of that option to have attributes on single line (for backwards compatibility)
- now we can have it 🎉 
- open discussion about changing the default behavior
- depending on the outcome:
  - change default behavior of the option
  - deprecate option
@lydell This probably also should have `lang:vue` as a label 😄 

Patch:
diff --git a/src/common/common-options.js b/src/common/common-options.js
--- a/src/common/common-options.js
+++ b/src/common/common-options.js
@@ -54,4 +54,11 @@ module.exports = {
     description:
       "Put > of opening tags on the last line instead of on a new line.",
   },
+  singleAttributePerLine: {
+    since: "2.3.3",
+    category: CATEGORY_COMMON,
+    type: "boolean",
+    default: false,
+    description: "Enforce single attribute per line in HTML, Vue and JSX.",
+  },
 };
diff --git a/src/language-html/options.js b/src/language-html/options.js
--- a/src/language-html/options.js
+++ b/src/language-html/options.js
@@ -28,6 +28,7 @@ module.exports = {
       },
     ],
   },
+  singleAttributePerLine: commonOptions.singleAttributePerLine,
   vueIndentScriptAndStyle: {
     since: "1.19.0",
     category: CATEGORY_HTML,
diff --git a/src/language-html/print/tag.js b/src/language-html/print/tag.js
--- a/src/language-html/print/tag.js
+++ b/src/language-html/print/tag.js
@@ -7,7 +7,7 @@
 const assert = require("assert");
 const { isNonEmptyArray } = require("../../common/util.js");
 const {
-  builders: { indent, join, line, softline },
+  builders: { indent, join, line, softline, hardline },
   utils: { replaceTextEndOfLine },
 } = require("../../document/index.js");
 const { locStart, locEnd } = require("../loc.js");
@@ -251,11 +251,14 @@ function printAttributes(path, options, print) {
     node.attrs[0].fullName === "src" &&
     node.children.length === 0;
 
+  const attributeLine =
+    options.singleAttributePerLine && node.attrs.length > 1 ? hardline : line;
+
   /** @type {Doc[]} */
   const parts = [
     indent([
       forceNotToBreakAttrContent ? " " : line,
-      join(line, printedAttributes),
+      join(attributeLine, printedAttributes),
     ]),
   ];
 
diff --git a/src/language-js/options.js b/src/language-js/options.js
--- a/src/language-js/options.js
+++ b/src/language-js/options.js
@@ -98,4 +98,5 @@ module.exports = {
       },
     ],
   },
+  singleAttributePerLine: commonOptions.singleAttributePerLine,
 };
diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js
--- a/src/language-js/print/jsx.js
+++ b/src/language-js/print/jsx.js
@@ -605,12 +605,17 @@ function printJsxOpeningElement(path, options, print) {
         attr.value.value.includes("\n")
     );
 
+  const attributeLine =
+    options.singleAttributePerLine && node.attributes.length > 1
+      ? hardline
+      : line;
+
   return group(
     [
       "<",
       print("name"),
       print("typeParameters"),
-      indent(path.map(() => [line, print()], "attributes")),
+      indent(path.map(() => [attributeLine, print()], "attributes")),
       node.selfClosing ? line : bracketSameLine ? ">" : softline,
       node.selfClosing ? "/>" : bracketSameLine ? "" : ">",
     ],


Test patch:
diff --git a/tests/format/html/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap b/tests/format/html/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
--- /dev/null
+++ b/tests/format/html/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,135 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`single-attribute-per-line.html - {"singleAttributePerLine":true} format 1`] = `
+====================================options=====================================
+parsers: ["html"]
+printWidth: 80
+singleAttributePerLine: true
+                                                                                | printWidth
+=====================================input======================================
+<div data-a="1">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="1" data-b="2" data-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<img src="/images/foo.png" />
+
+<img src="/images/foo.png" alt="bar" />
+
+<img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+
+=====================================output=====================================
+<div data-a="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
+
+<div
+  data-a="1"
+  data-b="2"
+  data-c="3"
+>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div
+  data-a="Lorem ipsum dolor sit amet"
+  data-b="Lorem ipsum dolor sit amet"
+  data-c="Lorem ipsum dolor sit amet"
+>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div
+  data-long-attribute-a="1"
+  data-long-attribute-b="2"
+  data-long-attribute-c="3"
+>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<img src="/images/foo.png" />
+
+<img
+  src="/images/foo.png"
+  alt="bar"
+/>
+
+<img
+  src="/images/foo.png"
+  alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+/>
+
+================================================================================
+`;
+
+exports[`single-attribute-per-line.html format 1`] = `
+====================================options=====================================
+parsers: ["html"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<div data-a="1">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="1" data-b="2" data-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<img src="/images/foo.png" />
+
+<img src="/images/foo.png" alt="bar" />
+
+<img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+
+=====================================output=====================================
+<div data-a="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
+
+<div data-a="1" data-b="2" data-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div
+  data-a="Lorem ipsum dolor sit amet"
+  data-b="Lorem ipsum dolor sit amet"
+  data-c="Lorem ipsum dolor sit amet"
+>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div
+  data-long-attribute-a="1"
+  data-long-attribute-b="2"
+  data-long-attribute-c="3"
+>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<img src="/images/foo.png" />
+
+<img src="/images/foo.png" alt="bar" />
+
+<img
+  src="/images/foo.png"
+  alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+/>
+
+================================================================================
+`;
diff --git a/tests/format/html/single-attribute-per-line/jsfmt.spec.js b/tests/format/html/single-attribute-per-line/jsfmt.spec.js
new file mode 100644
--- /dev/null
+++ b/tests/format/html/single-attribute-per-line/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["html"]);
+run_spec(__dirname, ["html"], { singleAttributePerLine: true });
diff --git a/tests/format/html/single-attribute-per-line/single-attribute-per-line.html b/tests/format/html/single-attribute-per-line/single-attribute-per-line.html
new file mode 100644
--- /dev/null
+++ b/tests/format/html/single-attribute-per-line/single-attribute-per-line.html
@@ -0,0 +1,21 @@
+<div data-a="1">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="1" data-b="2" data-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+</div>
+
+<img src="/images/foo.png" />
+
+<img src="/images/foo.png" alt="bar" />
+
+<img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
diff --git a/tests/format/jsx/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap b/tests/format/jsx/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
--- /dev/null
+++ b/tests/format/jsx/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,163 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`single-attribute-per-line.js - {"singleAttributePerLine":true} format 1`] = `
+====================================options=====================================
+parsers: ["flow", "babel", "typescript"]
+printWidth: 80
+singleAttributePerLine: true
+                                                                                | printWidth
+=====================================input======================================
+import React from "react";
+
+const Component = () => (
+  <div>
+    <div data-a="1">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="1" data-b="2" data-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <img src="/images/foo.png" />
+
+    <img src="/images/foo.png" alt="bar" />
+
+    <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+  </div>
+);
+
+=====================================output=====================================
+import React from "react";
+
+const Component = () => (
+  <div>
+    <div data-a="1">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div
+      data-a="1"
+      data-b="2"
+      data-c="3"
+    >
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div
+      data-a="Lorem ipsum dolor sit amet"
+      data-b="Lorem ipsum dolor sit amet"
+      data-c="Lorem ipsum dolor sit amet"
+    >
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div
+      data-long-attribute-a="1"
+      data-long-attribute-b="2"
+      data-long-attribute-c="3"
+    >
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <img src="/images/foo.png" />
+
+    <img
+      src="/images/foo.png"
+      alt="bar"
+    />
+
+    <img
+      src="/images/foo.png"
+      alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+    />
+  </div>
+);
+
+================================================================================
+`;
+
+exports[`single-attribute-per-line.js format 1`] = `
+====================================options=====================================
+parsers: ["flow", "babel", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+import React from "react";
+
+const Component = () => (
+  <div>
+    <div data-a="1">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="1" data-b="2" data-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <img src="/images/foo.png" />
+
+    <img src="/images/foo.png" alt="bar" />
+
+    <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+  </div>
+);
+
+=====================================output=====================================
+import React from "react";
+
+const Component = () => (
+  <div>
+    <div data-a="1">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="1" data-b="2" data-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div
+      data-a="Lorem ipsum dolor sit amet"
+      data-b="Lorem ipsum dolor sit amet"
+      data-c="Lorem ipsum dolor sit amet"
+    >
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div
+      data-long-attribute-a="1"
+      data-long-attribute-b="2"
+      data-long-attribute-c="3"
+    >
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <img src="/images/foo.png" />
+
+    <img src="/images/foo.png" alt="bar" />
+
+    <img
+      src="/images/foo.png"
+      alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+    />
+  </div>
+);
+
+================================================================================
+`;
diff --git a/tests/format/jsx/single-attribute-per-line/jsfmt.spec.js b/tests/format/jsx/single-attribute-per-line/jsfmt.spec.js
new file mode 100644
--- /dev/null
+++ b/tests/format/jsx/single-attribute-per-line/jsfmt.spec.js
@@ -0,0 +1,4 @@
+run_spec(__dirname, ["flow", "babel", "typescript"]);
+run_spec(__dirname, ["flow", "babel", "typescript"], {
+  singleAttributePerLine: true,
+});
diff --git a/tests/format/jsx/single-attribute-per-line/single-attribute-per-line.js b/tests/format/jsx/single-attribute-per-line/single-attribute-per-line.js
new file mode 100644
--- /dev/null
+++ b/tests/format/jsx/single-attribute-per-line/single-attribute-per-line.js
@@ -0,0 +1,27 @@
+import React from "react";
+
+const Component = () => (
+  <div>
+    <div data-a="1">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="1" data-b="2" data-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+    </div>
+
+    <img src="/images/foo.png" />
+
+    <img src="/images/foo.png" alt="bar" />
+
+    <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+  </div>
+);
diff --git a/tests/format/vue/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap b/tests/format/vue/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
--- /dev/null
+++ b/tests/format/vue/single-attribute-per-line/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,143 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`single-attribute-per-line.vue - {"singleAttributePerLine":true} format 1`] = `
+====================================options=====================================
+parsers: ["vue"]
+printWidth: 80
+singleAttributePerLine: true
+                                                                                | printWidth
+=====================================input======================================
+<template>
+  <div data-a="1">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="1" data-b="2" data-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <img src="/images/foo.png" />
+
+  <img src="/images/foo.png" alt="bar" />
+
+  <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+</template>
+
+=====================================output=====================================
+<template>
+  <div data-a="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
+
+  <div
+    data-a="1"
+    data-b="2"
+    data-c="3"
+  >
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div
+    data-a="Lorem ipsum dolor sit amet"
+    data-b="Lorem ipsum dolor sit amet"
+    data-c="Lorem ipsum dolor sit amet"
+  >
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div
+    data-long-attribute-a="1"
+    data-long-attribute-b="2"
+    data-long-attribute-c="3"
+  >
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <img src="/images/foo.png" />
+
+  <img
+    src="/images/foo.png"
+    alt="bar"
+  />
+
+  <img
+    src="/images/foo.png"
+    alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+  />
+</template>
+
+================================================================================
+`;
+
+exports[`single-attribute-per-line.vue format 1`] = `
+====================================options=====================================
+parsers: ["vue"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<template>
+  <div data-a="1">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="1" data-b="2" data-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <img src="/images/foo.png" />
+
+  <img src="/images/foo.png" alt="bar" />
+
+  <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+</template>
+
+=====================================output=====================================
+<template>
+  <div data-a="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
+
+  <div data-a="1" data-b="2" data-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div
+    data-a="Lorem ipsum dolor sit amet"
+    data-b="Lorem ipsum dolor sit amet"
+    data-c="Lorem ipsum dolor sit amet"
+  >
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div
+    data-long-attribute-a="1"
+    data-long-attribute-b="2"
+    data-long-attribute-c="3"
+  >
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <img src="/images/foo.png" />
+
+  <img src="/images/foo.png" alt="bar" />
+
+  <img
+    src="/images/foo.png"
+    alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+  />
+</template>
+
+================================================================================
+`;
diff --git a/tests/format/vue/single-attribute-per-line/jsfmt.spec.js b/tests/format/vue/single-attribute-per-line/jsfmt.spec.js
new file mode 100644
--- /dev/null
+++ b/tests/format/vue/single-attribute-per-line/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["vue"]);
+run_spec(__dirname, ["vue"], { singleAttributePerLine: true });
diff --git a/tests/format/vue/single-attribute-per-line/single-attribute-per-line.vue b/tests/format/vue/single-attribute-per-line/single-attribute-per-line.vue
new file mode 100644
--- /dev/null
+++ b/tests/format/vue/single-attribute-per-line/single-attribute-per-line.vue
@@ -0,0 +1,23 @@
+<template>
+  <div data-a="1">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="1" data-b="2" data-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-a="Lorem ipsum dolor sit amet" data-b="Lorem ipsum dolor sit amet" data-c="Lorem ipsum dolor sit amet">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <div data-long-attribute-a="1" data-long-attribute-b="2" data-long-attribute-c="3">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+  </div>
+
+  <img src="/images/foo.png" />
+
+  <img src="/images/foo.png" alt="bar" />
+
+  <img src="/images/foo.png" alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
+</template>
diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap
--- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap
+++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap
@@ -86,6 +86,9 @@ Format options:
                            Change when properties in objects are quoted.
                            Defaults to as-needed.
   --no-semi                Do not print semicolons, except at the beginning of lines which may need them.
+  --single-attribute-per-line
+                           Enforce single attribute per line in HTML, Vue and JSX.
+                           Defaults to false.
   --single-quote           Use single quotes instead of double quotes.
                            Defaults to false.
   --tab-width <int>        Number of spaces per indentation level.
@@ -251,6 +254,9 @@ Format options:
                            Change when properties in objects are quoted.
                            Defaults to as-needed.
   --no-semi                Do not print semicolons, except at the beginning of lines which may need them.
+  --single-attribute-per-line
+                           Enforce single attribute per line in HTML, Vue and JSX.
+                           Defaults to false.
   --single-quote           Use single quotes instead of double quotes.
                            Defaults to false.
   --tab-width <int>        Number of spaces per indentation level.
diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap
--- a/tests/integration/__tests__/__snapshots__/help-options.js.snap
+++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap
@@ -541,6 +541,19 @@ Default: true
 
 exports[`show detailed usage with --help semi (write) 1`] = `Array []`;
 
+exports[`show detailed usage with --help single-attribute-per-line (stderr) 1`] = `""`;
+
+exports[`show detailed usage with --help single-attribute-per-line (stdout) 1`] = `
+"--single-attribute-per-line
+
+  Enforce single attribute per line in HTML, Vue and JSX.
+
+Default: false
+"
+`;
+
+exports[`show detailed usage with --help single-attribute-per-line (write) 1`] = `Array []`;
+
 exports[`show detailed usage with --help single-quote (stderr) 1`] = `""`;
 
 exports[`show detailed usage with --help single-quote (stdout) 1`] = `
diff --git a/tests/integration/__tests__/__snapshots__/schema.js.snap b/tests/integration/__tests__/__snapshots__/schema.js.snap
--- a/tests/integration/__tests__/__snapshots__/schema.js.snap
+++ b/tests/integration/__tests__/__snapshots__/schema.js.snap
@@ -366,6 +366,11 @@ in order for it to be formatted.",
           "description": "Print semicolons.",
           "type": "boolean",
         },
+        "singleAttributePerLine": Object {
+          "default": false,
+          "description": "Enforce single attribute per line in HTML, Vue and JSX.",
+          "type": "boolean",
+        },
         "singleQuote": Object {
           "default": false,
           "description": "Use single quotes instead of double quotes.",
diff --git a/tests/integration/__tests__/__snapshots__/support-info.js.snap b/tests/integration/__tests__/__snapshots__/support-info.js.snap
--- a/tests/integration/__tests__/__snapshots__/support-info.js.snap
+++ b/tests/integration/__tests__/__snapshots__/support-info.js.snap
@@ -239,6 +239,10 @@ Object {
       "default": true,
       "type": "boolean",
     },
+    "singleAttributePerLine": Object {
+      "default": false,
+      "type": "boolean",
+    },
     "singleQuote": Object {
       "default": false,
       "type": "boolean",
@@ -1065,6 +1069,15 @@ exports[`CLI --support-info (stdout) 1`] = `
       \\"since\\": \\"1.0.0\\",
       \\"type\\": \\"boolean\\"
     },
+    {
+      \\"category\\": \\"Common\\",
+      \\"default\\": false,
+      \\"description\\": \\"Enforce single attribute per line in HTML, Vue and JSX.\\",
+      \\"name\\": \\"singleAttributePerLine\\",
+      \\"pluginDefaults\\": {},
+      \\"since\\": \\"2.3.3\\",
+      \\"type\\": \\"boolean\\"
+    },
     {
       \\"category\\": \\"Common\\",
       \\"default\\": false,