diff --git a/docs/rules/html-quotes.md b/docs/rules/html-quotes.md index aa0180de9..2d95009fe 100644 --- a/docs/rules/html-quotes.md +++ b/docs/rules/html-quotes.md @@ -43,13 +43,19 @@ Default is set to `double`. ```json { - "vue/html-quotes": ["error", "double" | "single"] + "vue/html-quotes": [ "error", "double" | "single", { "avoidEscape": false } ] } ``` +String option: + - `"double"` (default) ... requires double quotes. - `"single"` ... requires single quotes. +Object option: + +- `avoidEscape` ... If `true`, allows strings to use single-quotes or double-quotes so long as the string contains a quote that would have to be escaped otherwise. + ### `"single"` @@ -67,6 +73,23 @@ Default is set to `double`. +### `"double", { "avoidEscape": true }` + + + +```vue + +``` + + + ## :books: Further reading - [Style guide - Quoted attribute values](https://vuejs.org/v2/style-guide/#Quoted-attribute-values-strongly-recommended) diff --git a/lib/rules/html-quotes.js b/lib/rules/html-quotes.js index c8392abef..b27b9d98d 100644 --- a/lib/rules/html-quotes.js +++ b/lib/rules/html-quotes.js @@ -25,17 +25,25 @@ module.exports = { }, fixable: 'code', schema: [ - { enum: ['double', 'single'] } + { enum: ['double', 'single'] }, + { + type: 'object', + properties: { + avoidEscape: { + type: 'boolean' + } + }, + additionalProperties: false + } ] }, create (context) { const sourceCode = context.getSourceCode() const double = context.options[0] !== 'single' + const avoidEscape = context.options[1] && context.options[1].avoidEscape === true const quoteChar = double ? '"' : "'" const quoteName = double ? 'double quotes' : 'single quotes' - const quotePattern = double ? /"/g : /'/g - const quoteEscaped = double ? '"' : ''' let hasInvalidEOF return utils.defineTemplateBodyVisitor(context, { @@ -48,14 +56,35 @@ module.exports = { const firstChar = text[0] if (firstChar !== quoteChar) { + const quoted = (firstChar === "'" || firstChar === '"') + if (avoidEscape && quoted) { + const contentText = text.slice(1, -1) + if (contentText.includes(quoteChar)) { + return + } + } + context.report({ node: node.value, loc: node.value.loc, message: 'Expected to be enclosed by {{kind}}.', data: { kind: quoteName }, fix (fixer) { - const contentText = (firstChar === "'" || firstChar === '"') ? text.slice(1, -1) : text - const replacement = quoteChar + contentText.replace(quotePattern, quoteEscaped) + quoteChar + const contentText = quoted ? text.slice(1, -1) : text + + const fixToDouble = avoidEscape && !quoted && contentText.includes(quoteChar) + ? ( + double + ? contentText.includes("'") + : !contentText.includes('"') + ) + : double + + const quotePattern = fixToDouble ? /"/g : /'/g + const quoteEscaped = fixToDouble ? '"' : ''' + const fixQuoteChar = fixToDouble ? '"' : "'" + + const replacement = fixQuoteChar + contentText.replace(quotePattern, quoteEscaped) + fixQuoteChar return fixer.replaceText(node.value, replacement) } }) diff --git a/tests/lib/rules/html-quotes.js b/tests/lib/rules/html-quotes.js index f54cc1d50..95338825a 100644 --- a/tests/lib/rules/html-quotes.js +++ b/tests/lib/rules/html-quotes.js @@ -55,6 +55,17 @@ tester.run('html-quotes', rule, { code: "", options: ['single'] }, + // avoidEscape + { + filename: 'test.vue', + code: "", + options: ['double', { avoidEscape: true }] + }, + { + filename: 'test.vue', + code: "", + options: ['single', { avoidEscape: true }] + }, // Invalid EOF { @@ -166,6 +177,49 @@ tester.run('html-quotes', rule, { output: "", options: ['single'], errors: ['Expected to be enclosed by single quotes.'] + }, + // avoidEscape + { + filename: 'test.vue', + code: "", + output: '', + options: ['double', { avoidEscape: true }], + errors: ['Expected to be enclosed by double quotes.'] + }, + { + filename: 'test.vue', + code: '', + output: "", + options: ['single', { avoidEscape: true }], + errors: ['Expected to be enclosed by single quotes.'] + }, + { + filename: 'test.vue', + code: '', + output: '', + options: ['double', { avoidEscape: true }], + errors: ['Expected to be enclosed by double quotes.'] + }, + { + filename: 'test.vue', + code: '', + output: "", + options: ['single', { avoidEscape: true }], + errors: ['Expected to be enclosed by single quotes.'] + }, + { + filename: 'test.vue', + code: '', + output: '', + options: ['double', { avoidEscape: true }], + errors: ['Expected to be enclosed by double quotes.'] + }, + { + filename: 'test.vue', + code: '', + output: '', + options: ['single', { avoidEscape: true }], + errors: ['Expected to be enclosed by single quotes.'] } ] })