UNPKG

eslint-plugin-stylistic-compat

Version:

Compatibility version of @stylistic/eslint-plugin for Node.js >= 12 and ESLint v8

304 lines (300 loc) 13 kB
'use strict'; var _Array$from = require('@babel/runtime-corejs3/core-js/array/from'); var _atInstanceProperty = require('@babel/runtime-corejs3/core-js/instance/at'); var utils = require('../utils.js'); require('@babel/runtime-corejs3/core-js/object/from-entries'); require('@babel/runtime-corejs3/core-js/instance/map'); require('@babel/runtime-corejs3/core-js/instance/filter'); require('@babel/runtime-corejs3/core-js/object/entries'); require('@babel/runtime-corejs3/core-js/set'); require('@babel/runtime-corejs3/core-js/instance/flags'); require('@babel/runtime-corejs3/core-js/instance/includes'); require('@babel/runtime-corejs3/core-js/instance/starts-with'); require('../vendor.js'); require('@babel/runtime-corejs3/core-js/object/assign'); require('@babel/runtime-corejs3/core-js/object/create'); require('@babel/runtime-corejs3/core-js/object/freeze'); require('@babel/runtime-corejs3/core-js/array/is-array'); require('@babel/runtime-corejs3/core-js/instance/last-index-of'); require('@babel/runtime-corejs3/core-js/instance/slice'); require('@babel/runtime-corejs3/core-js/object/define-properties'); require('@babel/runtime-corejs3/core-js/object/keys'); require('@babel/runtime-corejs3/core-js/instance/index-of'); require('@babel/runtime-corejs3/core-js/symbol'); require('@babel/runtime-corejs3/core-js/symbol/iterator'); require('@babel/runtime-corejs3/core-js/parse-int'); require('@babel/runtime-corejs3/core-js/parse-float'); require('@babel/runtime-corejs3/core-js/weak-map'); require('@babel/runtime-corejs3/core-js/object/define-property'); require('@babel/runtime-corejs3/core-js/global-this'); require('@babel/runtime-corejs3/core-js/instance/for-each'); require('eslint/use-at-your-own-risk'); require('eslint'); require('@babel/runtime-corejs3/core-js/object/get-own-property-descriptor'); require('@babel/runtime-corejs3/core-js/map'); require('@babel/runtime-corejs3/core-js/instance/some'); require('@babel/runtime-corejs3/core-js/instance/find'); require('@babel/runtime-corejs3/core-js/instance/every'); require('@babel/runtime-corejs3/core-js/instance/sort'); require('@babel/runtime-corejs3/core-js/array/of'); require('@babel/runtime-corejs3/core-js/instance/concat'); require('@babel/runtime-corejs3/core-js/instance/entries'); require('@babel/runtime-corejs3/core-js/instance/find-index'); require('@babel/runtime-corejs3/core-js/instance/flat'); require('@babel/runtime-corejs3/core-js/instance/keys'); require('@babel/runtime-corejs3/core-js/instance/values'); require('@babel/runtime-corejs3/core-js/object/get-own-property-names'); require('@babel/runtime-corejs3/core-js/number/is-finite'); require('@babel/runtime-corejs3/core-js/number/is-nan'); require('@babel/runtime-corejs3/core-js/number/parse-float'); require('@babel/runtime-corejs3/core-js/number/parse-int'); require('@babel/runtime-corejs3/core-js/object/is'); require('@babel/runtime-corejs3/core-js/object/is-extensible'); require('@babel/runtime-corejs3/core-js/object/is-frozen'); require('@babel/runtime-corejs3/core-js/object/is-sealed'); require('@babel/runtime-corejs3/core-js/object/values'); require('@babel/runtime-corejs3/core-js/string/from-code-point'); require('@babel/runtime-corejs3/core-js/string/raw'); require('@babel/runtime-corejs3/core-js/instance/code-point-at'); require('@babel/runtime-corejs3/core-js/instance/ends-with'); require('@babel/runtime-corejs3/core-js/instance/pad-end'); require('@babel/runtime-corejs3/core-js/instance/pad-start'); require('@babel/runtime-corejs3/core-js/instance/trim'); require('@babel/runtime-corejs3/core-js/instance/trim-end'); require('@babel/runtime-corejs3/core-js/instance/trim-left'); require('@babel/runtime-corejs3/core-js/instance/trim-right'); require('@babel/runtime-corejs3/core-js/instance/trim-start'); require('@babel/runtime-corejs3/core-js/symbol/for'); require('@babel/runtime-corejs3/core-js/symbol/key-for'); require('@babel/runtime-corejs3/core-js/object/prevent-extensions'); require('@babel/runtime-corejs3/core-js/object/seal'); require('@babel/runtime-corejs3/core-js/object/get-prototype-of'); require('@babel/runtime-corejs3/core-js/symbol/replace'); require('@babel/runtime-corejs3/core-js/instance/bind'); require('@babel/runtime-corejs3/core-js/instance/splice'); require('@babel/runtime-corejs3/core-js/instance/repeat'); require('@babel/runtime-corejs3/core-js/instance/reduce'); require('@babel/runtime-corejs3/core-js/instance/reverse'); const messages = { bracketLocation: "The closing bracket must be {{location}}{{details}}" }; var jsxClosingBracketLocation = utils.createRule({ name: "jsx-closing-bracket-location", package: "jsx", meta: { type: "layout", docs: { description: "Enforce closing bracket location in JSX" }, fixable: "code", messages, schema: [{ anyOf: [{ type: "string", enum: ["after-props", "props-aligned", "tag-aligned", "line-aligned"] }, { type: "object", properties: { location: { type: "string", enum: ["after-props", "props-aligned", "tag-aligned", "line-aligned"] } }, additionalProperties: false }, { type: "object", properties: { nonEmpty: { oneOf: [{ type: "string", enum: ["after-props", "props-aligned", "tag-aligned", "line-aligned"] }, { type: "boolean", enum: [false] }] }, selfClosing: { oneOf: [{ type: "string", enum: ["after-props", "props-aligned", "tag-aligned", "line-aligned"] }, { type: "boolean", enum: [false] }] } }, additionalProperties: false }] }] }, create(context) { const MESSAGE_LOCATION = { "after-props": "placed after the last prop", "after-tag": "placed after the opening tag", "props-aligned": "aligned with the last prop", "tag-aligned": "aligned with the opening tag", "line-aligned": "aligned with the line containing the opening tag" }; const DEFAULT_LOCATION = "tag-aligned"; const config = context.options[0]; const options = { nonEmpty: DEFAULT_LOCATION, selfClosing: DEFAULT_LOCATION }; if (typeof config === "string") { options.nonEmpty = config; options.selfClosing = config; } else if (typeof config === "object") { if ("location" in config) { options.nonEmpty = config.location; options.selfClosing = config.location; } if ("nonEmpty" in config) options.nonEmpty = config.nonEmpty; if ("selfClosing" in config) options.selfClosing = config.selfClosing; } function getExpectedLocation(tokens) { let location; if (typeof tokens.lastProp === "undefined") location = "after-tag";else if (tokens.opening.line === tokens.lastProp.lastLine) location = "after-props";else location = tokens.selfClosing ? options.selfClosing : options.nonEmpty; return location; } function getCorrectColumn(tokens, expectedLocation) { switch (expectedLocation) { case "props-aligned": return tokens.lastProp.column; case "tag-aligned": return tokens.opening.column; case "line-aligned": return tokens.openingStartOfLine.column; default: return null; } } function hasCorrectLocation(tokens, expectedLocation) { switch (expectedLocation) { case "after-tag": return tokens.tag.line === tokens.closing.line; case "after-props": return tokens.lastProp.lastLine === tokens.closing.line; case "props-aligned": case "tag-aligned": case "line-aligned": { const correctColumn = getCorrectColumn(tokens, expectedLocation); return correctColumn === tokens.closing.column; } default: return true; } } function getIndentation(tokens, expectedLocation, correctColumn) { const newColumn = correctColumn || 0; let indentation; let spaces = []; switch (expectedLocation) { case "props-aligned": indentation = /^\s*/.exec(context.sourceCode.lines[tokens.lastProp.firstLine - 1])[0]; break; case "tag-aligned": case "line-aligned": indentation = /^\s*/.exec(context.sourceCode.lines[tokens.opening.line - 1])[0]; break; default: indentation = ""; } if (indentation.length + 1 < newColumn) { spaces = _Array$from({ length: +correctColumn + 1 - indentation.length }); } return indentation + spaces.join(" "); } function getTokensLocations(node) { var _exec; const sourceCode = context.sourceCode; const opening = sourceCode.getFirstToken(node).loc.start; const closing = sourceCode.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start; const tag = sourceCode.getFirstToken(node.name).loc.start; let lastProp; if (node.attributes.length) { lastProp = node.attributes[node.attributes.length - 1]; lastProp = { column: sourceCode.getFirstToken(lastProp).loc.start.column, firstLine: sourceCode.getFirstToken(lastProp).loc.start.line, lastLine: sourceCode.getLastToken(lastProp).loc.end.line }; } const openingLine = sourceCode.lines[opening.line - 1]; const closingLine = sourceCode.lines[closing.line - 1]; const isTab = { openTab: /^\t/.test(openingLine), closeTab: /^\t/.test(closingLine) }; const openingStartOfLine = { column: (_exec = /^\s*/.exec(openingLine)) == null ? undefined : _exec[0].length, line: opening.line }; return { isTab, tag, opening, closing, lastProp, selfClosing: node.selfClosing, openingStartOfLine }; } return { "JSXOpeningElement:exit": function (node) { var _context, _context2; const lastAttributeNode = _atInstanceProperty(_context = node.attributes).call(_context, -1); const cachedLastAttributeEndPos = lastAttributeNode ? lastAttributeNode.range[1] : null; let expectedNextLine; const tokens = getTokensLocations(node); let expectedLocation = getExpectedLocation(tokens); let usingSameIndentation = true; if (expectedLocation === "tag-aligned") usingSameIndentation = tokens.isTab.openTab === tokens.isTab.closeTab; const lastComment = _atInstanceProperty(_context2 = context.sourceCode.getCommentsInside(node)).call(_context2, -1); const hasTrailingComment = lastComment && lastComment.range[0] > (lastAttributeNode != null ? lastAttributeNode : node.name).range[1]; if ((expectedLocation === "after-props" || expectedLocation === "after-tag") && !(hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) && hasTrailingComment) { expectedLocation = "line-aligned"; } if (hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) return; const data = { location: MESSAGE_LOCATION[expectedLocation], details: "" }; const correctColumn = getCorrectColumn(tokens, expectedLocation); if (correctColumn !== null) { expectedNextLine = tokens.lastProp && tokens.lastProp.lastLine === tokens.closing.line; data.details = ` (expected column ${correctColumn + 1}${expectedNextLine ? " on the next line)" : ")"}`; } context.report({ node, messageId: "bracketLocation", loc: tokens.closing, data, fix(fixer) { const closingTag = tokens.selfClosing ? "/>" : ">"; switch (expectedLocation) { case "after-tag": if (cachedLastAttributeEndPos) return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]], (expectedNextLine ? "\n" : "") + closingTag); return fixer.replaceTextRange([node.name.range[1], node.range[1]], (expectedNextLine ? "\n" : " ") + closingTag); case "after-props": return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]], (expectedNextLine ? "\n" : "") + closingTag); case "props-aligned": case "tag-aligned": case "line-aligned": { const rangeStart = hasTrailingComment ? lastComment.range[1] : cachedLastAttributeEndPos; return fixer.replaceTextRange([rangeStart, node.range[1]], ` ${getIndentation(tokens, expectedLocation, correctColumn)}${closingTag}`); } } return null; } }); } }; } }); module.exports = jsxClosingBracketLocation;