UNPKG

eslint-plugin-react-dom

Version:

ESLint React's ESLint plugin for React DOM related rules.

1,847 lines (1,839 loc) • 57 kB
'use strict'; var shared = require('@eslint-react/shared'); var ER = require('@eslint-react/core'); var utils = require('@typescript-eslint/utils'); var AST = require('@eslint-react/ast'); var types = require('@typescript-eslint/types'); var compareVersions = require('compare-versions'); var kit = require('@eslint-react/kit'); var eff = require('@eslint-react/eff'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var ER__namespace = /*#__PURE__*/_interopNamespace(ER); var AST__namespace = /*#__PURE__*/_interopNamespace(AST); var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name3 in all) __defProp(target, name3, { get: all[name3], enumerable: true }); }; // src/configs/recommended.ts var recommended_exports = {}; __export(recommended_exports, { name: () => name, rules: () => rules, settings: () => settings }); var name = "react-dom/recommended"; var rules = { "react-dom/no-dangerously-set-innerhtml": "warn", "react-dom/no-dangerously-set-innerhtml-with-children": "error", "react-dom/no-find-dom-node": "error", "react-dom/no-flush-sync": "error", "react-dom/no-hydrate": "error", "react-dom/no-missing-button-type": "warn", "react-dom/no-missing-iframe-sandbox": "warn", "react-dom/no-namespace": "error", "react-dom/no-render": "error", "react-dom/no-render-return-value": "error", "react-dom/no-script-url": "warn", "react-dom/no-unsafe-iframe-sandbox": "warn", "react-dom/no-unsafe-target-blank": "warn", "react-dom/no-use-form-state": "error", "react-dom/no-void-elements-with-children": "error" }; var settings = { "react-x": shared.DEFAULT_ESLINT_REACT_SETTINGS }; // package.json var name2 = "eslint-plugin-react-dom"; var version = "1.52.3"; function createJsxElementResolver(context) { const { components, polymorphicPropName } = shared.getSettingsFromContext(context); return { resolve(node) { const name3 = ER__namespace.getElementType(context, node); const component = components.findLast((c) => c.name === name3 || c.re.test(name3)); const result = { attributes: component?.attributes ?? [], domElementType: component?.as ?? name3, jsxElementType: name3 }; if (name3 === name3.toLowerCase() || component != null || polymorphicPropName == null) { return result; } const initialScope = context.sourceCode.getScope(node); const polymorphicPropAttr = ER__namespace.getAttribute( context, polymorphicPropName, node.openingElement.attributes, initialScope ); if (polymorphicPropAttr != null) { const polymorphicPropValue = ER__namespace.getAttributeValue( context, polymorphicPropAttr, polymorphicPropName ); if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") { return { ...result, domElementType: polymorphicPropValue.value }; } } return result; } }; } var createRule = utils.ESLintUtils.RuleCreator(shared.getDocsUrl("dom")); // src/utils/find-custom-component.ts function findCustomComponentProp(name3, props) { return props.findLast((a) => a.as === name3); } // src/rules/no-dangerously-set-innerhtml.ts var RULE_NAME = "no-dangerously-set-innerhtml"; var RULE_FEATURES = []; var no_dangerously_set_innerhtml_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `dangerouslySetInnerHTML`.", [Symbol.for("rule_features")]: RULE_FEATURES }, messages: { noDangerouslySetInnerhtml: "Using 'dangerouslySetInnerHTML' may have security implications." }, schema: [] }, name: RULE_NAME, create, defaultOptions: [] }); var dangerouslySetInnerHTML = "dangerouslySetInnerHTML"; function create(context) { if (!context.sourceCode.text.includes(dangerouslySetInnerHTML)) return {}; return { JSXElement(node) { const attribute = ER__namespace.getAttribute( context, dangerouslySetInnerHTML, node.openingElement.attributes, context.sourceCode.getScope(node) ); if (attribute == null) return; context.report({ messageId: "noDangerouslySetInnerhtml", node: attribute }); } }; } var RULE_NAME2 = "no-dangerously-set-innerhtml-with-children"; var RULE_FEATURES2 = []; var no_dangerously_set_innerhtml_with_children_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `dangerouslySetInnerHTML` and `children` at the same time.", [Symbol.for("rule_features")]: RULE_FEATURES2 }, messages: { noDangerouslySetInnerhtmlWithChildren: "A DOM component cannot use both 'children' and 'dangerouslySetInnerHTML'." }, schema: [] }, name: RULE_NAME2, create: create2, defaultOptions: [] }); var dangerouslySetInnerHTML2 = "dangerouslySetInnerHTML"; function create2(context) { if (!context.sourceCode.text.includes(dangerouslySetInnerHTML2)) return {}; return { JSXElement(node) { const attributes = node.openingElement.attributes; const initialScope = context.sourceCode.getScope(node); const hasChildren = hasChildrenWithin(node) || ER__namespace.hasAttribute(context, "children", attributes, initialScope); if (hasChildren && ER__namespace.hasAttribute(context, dangerouslySetInnerHTML2, attributes, initialScope)) { context.report({ messageId: "noDangerouslySetInnerhtmlWithChildren", node }); } } }; } function hasChildrenWithin(node) { return node.children.length > 0 && node.children[0] != null && !AST__namespace.isLineBreak(node.children[0]); } var RULE_NAME3 = "no-find-dom-node"; var RULE_FEATURES3 = []; var no_find_dom_node_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `findDOMNode`.", [Symbol.for("rule_features")]: RULE_FEATURES3 }, messages: { noFindDomNode: "[Deprecated] Use alternatives instead." }, schema: [] }, name: RULE_NAME3, create: create3, defaultOptions: [] }); var findDOMNode = "findDOMNode"; function create3(context) { if (!context.sourceCode.text.includes(findDOMNode)) return {}; return { CallExpression(node) { const { callee } = node; switch (callee.type) { case types.AST_NODE_TYPES.Identifier: if (callee.name === findDOMNode) { context.report({ messageId: "noFindDomNode", node }); } return; case types.AST_NODE_TYPES.MemberExpression: if (callee.property.type === types.AST_NODE_TYPES.Identifier && callee.property.name === findDOMNode) { context.report({ messageId: "noFindDomNode", node }); } return; } } }; } var RULE_NAME4 = "no-flush-sync"; var RULE_FEATURES4 = []; var no_flush_sync_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `flushSync`.", [Symbol.for("rule_features")]: RULE_FEATURES4 }, messages: { noFlushSync: "Using 'flushSync' is uncommon and can hurt the performance of your app." }, schema: [] }, name: RULE_NAME4, create: create4, defaultOptions: [] }); var flushSync = "flushSync"; function create4(context) { if (!context.sourceCode.text.includes(flushSync)) return {}; return { CallExpression(node) { const { callee } = node; switch (callee.type) { case types.AST_NODE_TYPES.Identifier: if (callee.name === flushSync) { context.report({ messageId: "noFlushSync", node }); } return; case types.AST_NODE_TYPES.MemberExpression: if (callee.property.type === types.AST_NODE_TYPES.Identifier && callee.property.name === flushSync) { context.report({ messageId: "noFlushSync", node }); } return; } } }; } var RULE_NAME5 = "no-hydrate"; var RULE_FEATURES5 = [ "MOD" ]; var no_hydrate_default = createRule({ meta: { type: "problem", docs: { description: "Replaces usages of `ReactDom.hydrate()` with `hydrateRoot()`.", [Symbol.for("rule_features")]: RULE_FEATURES5 }, fixable: "code", messages: { noHydrate: "[Deprecated] Use 'hydrateRoot()' instead." }, schema: [] }, name: RULE_NAME5, create: create5, defaultOptions: [] }); var hydrate = "hydrate"; function create5(context) { if (!context.sourceCode.text.includes(hydrate)) return {}; const settings2 = shared.getSettingsFromContext(context); if (compareVersions.compare(settings2.version, "18.0.0", "<")) return {}; const reactDomNames = /* @__PURE__ */ new Set(); const hydrateNames = /* @__PURE__ */ new Set(); return { CallExpression(node) { switch (true) { case (node.callee.type === types.AST_NODE_TYPES.Identifier && hydrateNames.has(node.callee.name)): context.report({ messageId: "noHydrate", node, fix: getFix(context, node) }); return; case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.object.type === types.AST_NODE_TYPES.Identifier && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === hydrate && reactDomNames.has(node.callee.object.name)): context.report({ messageId: "noHydrate", node, fix: getFix(context, node) }); return; } }, ImportDeclaration(node) { const [baseSource] = node.source.value.split("/"); if (baseSource !== "react-dom") return; for (const specifier of node.specifiers) { switch (specifier.type) { case types.AST_NODE_TYPES.ImportSpecifier: if (specifier.imported.type !== types.AST_NODE_TYPES.Identifier) continue; if (specifier.imported.name === hydrate) { hydrateNames.add(specifier.local.name); } continue; case types.AST_NODE_TYPES.ImportDefaultSpecifier: case types.AST_NODE_TYPES.ImportNamespaceSpecifier: reactDomNames.add(specifier.local.name); continue; } } } }; } function getFix(context, node) { const getText2 = (n) => context.sourceCode.getText(n); return (fixer) => { const [arg0, arg1] = node.arguments; if (arg0 == null || arg1 == null) return null; return [ fixer.insertTextBefore(context.sourceCode.ast, 'import { hydrateRoot } from "react-dom/client";\n'), fixer.replaceText(node, `hydrateRoot(${getText2(arg1)}, ${getText2(arg0)})`) ]; }; } var RULE_NAME6 = "no-missing-button-type"; var RULE_FEATURES6 = []; var no_missing_button_type_default = createRule({ meta: { type: "problem", docs: { description: "Enforces explicit `type` attribute for `button` elements.", [Symbol.for("rule_features")]: RULE_FEATURES6 }, messages: { noMissingButtonType: "Add missing 'type' attribute on 'button' component." }, schema: [] }, name: RULE_NAME6, create: create6, defaultOptions: [] }); function create6(context) { const resolver = createJsxElementResolver(context); return { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "button") return; const customComponentProp = findCustomComponentProp("type", attributes); const propNameOnJsx = customComponentProp?.name ?? "type"; const attributeNode = ER__namespace.getAttribute( context, propNameOnJsx, node.openingElement.attributes, context.sourceCode.getScope(node) ); if (attributeNode != null) { const attributeValue = ER__namespace.getAttributeValue( context, attributeNode, propNameOnJsx ); if (attributeValue.kind === "some" && typeof attributeValue.value !== "string") { context.report({ messageId: "noMissingButtonType", node: attributeNode }); } return; } if (typeof customComponentProp?.defaultValue !== "string") { context.report({ messageId: "noMissingButtonType", node }); } } }; } var RULE_NAME7 = "no-missing-iframe-sandbox"; var RULE_FEATURES7 = []; var validTypes = [ "", "allow-downloads", "allow-downloads-without-user-activation", "allow-forms", "allow-modals", "allow-orientation-lock", "allow-pointer-lock", "allow-popups", "allow-popups-to-escape-sandbox", "allow-presentation", "allow-same-origin", "allow-scripts", "allow-storage-access-by-user-activation", "allow-top-navigation", "allow-top-navigation-by-user-activation", "allow-top-navigation-to-custom-protocols" ]; function hasValidSandBox(value) { return typeof value === "string" && value.split(" ").every((value2) => validTypes.some((valid) => valid === value2)); } var no_missing_iframe_sandbox_default = createRule({ meta: { type: "problem", docs: { description: "Enforces explicit `sandbox` attribute for `iframe` elements.", [Symbol.for("rule_features")]: RULE_FEATURES7 }, messages: { noMissingIframeSandbox: "Add missing 'sandbox' attribute on 'iframe' component." }, schema: [] }, name: RULE_NAME7, create: create7, defaultOptions: [] }); function create7(context) { const resolver = createJsxElementResolver(context); return { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "iframe") return; const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; const attributeNode = ER__namespace.getAttribute( context, propNameOnJsx, node.openingElement.attributes, context.sourceCode.getScope(node) ); if (attributeNode != null) { const attributeValue = ER__namespace.getAttributeValue( context, attributeNode, propNameOnJsx ); if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return; context.report({ messageId: "noMissingIframeSandbox", node: attributeNode }); return; } if (!hasValidSandBox(customComponentProp?.defaultValue)) { context.report({ messageId: "noMissingIframeSandbox", node }); } } }; } var RULE_NAME8 = "no-namespace"; var RULE_FEATURES8 = []; var no_namespace_default = createRule({ meta: { type: "problem", docs: { description: "Enforces the absence of a `namespace` in React elements.", [Symbol.for("rule_features")]: RULE_FEATURES8 }, messages: { noNamespace: "A React component '{{name}}' must not be in a namespace, as React does not support them." }, schema: [] }, name: RULE_NAME8, create: create8, defaultOptions: [] }); function create8(context) { return { JSXElement(node) { const name3 = ER__namespace.getElementType(context, node); if (typeof name3 !== "string" || !name3.includes(":")) { return; } context.report({ messageId: "noNamespace", node: node.openingElement.name, data: { name: name3 } }); } }; } var RULE_NAME9 = "no-render"; var RULE_FEATURES9 = [ "MOD" ]; var no_render_default = createRule({ meta: { type: "problem", docs: { description: "Replaces usages of `ReactDom.render()` with `createRoot(node).render()`.", [Symbol.for("rule_features")]: RULE_FEATURES9 }, fixable: "code", messages: { noRender: "[Deprecated] Use 'createRoot(node).render()' instead." }, schema: [] }, name: RULE_NAME9, create: create9, defaultOptions: [] }); function create9(context) { if (!context.sourceCode.text.includes("render")) return {}; const settings2 = shared.getSettingsFromContext(context); if (compareVersions.compare(settings2.version, "18.0.0", "<")) return {}; const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDom"]); const renderNames = /* @__PURE__ */ new Set(); return { CallExpression(node) { switch (true) { case (node.callee.type === types.AST_NODE_TYPES.Identifier && renderNames.has(node.callee.name)): context.report({ messageId: "noRender", node, fix: getFix2(context, node) }); return; case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.object.type === types.AST_NODE_TYPES.Identifier && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === "render" && reactDomNames.has(node.callee.object.name)): context.report({ messageId: "noRender", node, fix: getFix2(context, node) }); return; } }, ImportDeclaration(node) { const [baseSource] = node.source.value.split("/"); if (baseSource !== "react-dom") return; for (const specifier of node.specifiers) { switch (specifier.type) { case types.AST_NODE_TYPES.ImportSpecifier: if (specifier.imported.type !== types.AST_NODE_TYPES.Identifier) continue; if (specifier.imported.name === "render") { renderNames.add(specifier.local.name); } continue; case types.AST_NODE_TYPES.ImportDefaultSpecifier: case types.AST_NODE_TYPES.ImportNamespaceSpecifier: reactDomNames.add(specifier.local.name); continue; } } } }; } function getFix2(context, node) { const getText2 = (n) => context.sourceCode.getText(n); return (fixer) => { const [arg0, arg1] = node.arguments; if (arg0 == null || arg1 == null) return null; return [ fixer.insertTextBefore(context.sourceCode.ast, 'import { createRoot } from "react-dom/client";\n'), fixer.replaceText(node, `createRoot(${getText2(arg1)}).render(${getText2(arg0)})`) ]; }; } var RULE_NAME10 = "no-render-return-value"; var RULE_FEATURES10 = []; var banParentTypes = [ types.AST_NODE_TYPES.VariableDeclarator, types.AST_NODE_TYPES.Property, types.AST_NODE_TYPES.ReturnStatement, types.AST_NODE_TYPES.ArrowFunctionExpression, types.AST_NODE_TYPES.AssignmentExpression ]; var no_render_return_value_default = createRule({ meta: { type: "problem", docs: { description: "Disallow the return value of `ReactDOM.render`.", [Symbol.for("rule_features")]: RULE_FEATURES10 }, messages: { noRenderReturnValue: "Do not depend on the return value from 'ReactDOM.render'." }, schema: [] }, name: RULE_NAME10, create: create10, defaultOptions: [] }); function create10(context) { const reactDomNames = /* @__PURE__ */ new Set(["ReactDOM", "ReactDom"]); const renderNames = /* @__PURE__ */ new Set(); return { CallExpression(node) { switch (true) { case (node.callee.type === types.AST_NODE_TYPES.Identifier && renderNames.has(node.callee.name) && banParentTypes.includes(node.parent.type)): context.report({ messageId: "noRenderReturnValue", node }); return; case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.object.type === types.AST_NODE_TYPES.Identifier && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === "render" && reactDomNames.has(node.callee.object.name) && banParentTypes.includes(node.parent.type)): context.report({ messageId: "noRenderReturnValue", node }); return; } }, ImportDeclaration(node) { const [baseSource] = node.source.value.split("/"); if (baseSource !== "react-dom") return; for (const specifier of node.specifiers) { switch (specifier.type) { case types.AST_NODE_TYPES.ImportSpecifier: if (specifier.imported.type !== types.AST_NODE_TYPES.Identifier) continue; if (specifier.imported.name === "render") { renderNames.add(specifier.local.name); } continue; case types.AST_NODE_TYPES.ImportDefaultSpecifier: case types.AST_NODE_TYPES.ImportNamespaceSpecifier: reactDomNames.add(specifier.local.name); continue; } } } }; } var RULE_NAME11 = "no-script-url"; var RULE_FEATURES11 = []; var no_script_url_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `javascript:` URLs as attribute values.", [Symbol.for("rule_features")]: RULE_FEATURES11 }, messages: { noScriptUrl: "Using a `javascript:` URL is a security risk and should be avoided." }, schema: [] }, name: RULE_NAME11, create: create11, defaultOptions: [] }); function create11(context) { return { JSXAttribute(node) { if (node.name.type !== types.AST_NODE_TYPES.JSXIdentifier || node.value == null) { return; } const attributeValue = ER__namespace.getAttributeValue(context, node, ER__namespace.getAttributeName(context, node)); if (attributeValue.kind === "none" || typeof attributeValue.value !== "string") return; if (kit.RegExp.JAVASCRIPT_PROTOCOL.test(attributeValue.value)) { context.report({ messageId: "noScriptUrl", node: node.value }); } } }; } var RULE_NAME12 = "no-unknown-property"; var DEFAULTS = { ignore: [], requireDataLowercase: false }; var DOM_ATTRIBUTE_NAMES = { "accept-charset": "acceptCharset", class: "className", crossorigin: "crossOrigin", for: "htmlFor", "http-equiv": "httpEquiv", nomodule: "noModule" }; var ATTRIBUTE_TAGS_MAP = { as: ["link"], abbr: ["th", "td"], align: [ "applet", "caption", "col", "colgroup", "hr", "iframe", "img", "table", "tbody", "td", "tfoot", "th", "thead", "tr" ], // deprecated, but known allowFullScreen: ["iframe", "video"], autoPictureInPicture: ["video"], charset: ["meta"], checked: ["input"], controls: ["audio", "video"], controlsList: ["audio", "video"], // image is required for SVG support, all other tags are HTML. crossOrigin: ["script", "img", "video", "audio", "link", "image"], disablePictureInPicture: ["video"], disableRemotePlayback: ["audio", "video"], displaystyle: ["math"], // https://html.spec.whatwg.org/multipage/links.html#downloading-resources download: ["a", "area"], fill: [ // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill // Fill color "altGlyph", "circle", "ellipse", "g", "line", "marker", "mask", "path", "polygon", "polyline", "rect", "svg", "symbol", "text", "textPath", "tref", "tspan", "use", // Animation final state "animate", "animateColor", "animateMotion", "animateTransform", "set" ], focusable: ["svg"], imageSizes: ["link"], imageSrcSet: ["link"], loop: ["audio", "video"], mozAllowFullScreen: ["iframe", "video"], muted: ["audio", "video"], noModule: ["script"], // Media events allowed only on audio and video tags, see https://github.com/facebook/react/blob/256aefbea1449869620fb26f6ec695536ab453f5/CHANGELOG.md#notable-enhancements onAbort: ["audio", "video"], onCanPlay: ["audio", "video"], onCanPlayThrough: ["audio", "video"], onCancel: ["dialog"], onClose: ["dialog"], onDurationChange: ["audio", "video"], onEmptied: ["audio", "video"], onEncrypted: ["audio", "video"], onEnded: ["audio", "video"], onError: ["audio", "video", "img", "link", "source", "script", "picture", "iframe"], onLoad: ["script", "img", "link", "picture", "iframe", "object", "source"], onLoadStart: ["audio", "video"], onLoadedData: ["audio", "video"], onLoadedMetadata: ["audio", "video"], onPause: ["audio", "video"], onPlay: ["audio", "video"], onPlaying: ["audio", "video"], onProgress: ["audio", "video"], onRateChange: ["audio", "video"], onResize: ["audio", "video"], onSeeked: ["audio", "video"], onSeeking: ["audio", "video"], onStalled: ["audio", "video"], onSuspend: ["audio", "video"], onTimeUpdate: ["audio", "video"], onVolumeChange: ["audio", "video"], onWaiting: ["audio", "video"], playsInline: ["video"], poster: ["video"], preload: ["audio", "video"], property: ["meta"], returnValue: ["dialog"], scrolling: ["iframe"], valign: ["tr", "td", "th", "thead", "tbody", "tfoot", "colgroup", "col"], // deprecated, but known viewBox: ["marker", "pattern", "svg", "symbol", "view"], webkitAllowFullScreen: ["iframe", "video"], webkitDirectory: ["input"] }; var SVGDOM_ATTRIBUTE_NAMES = { "accent-height": "accentHeight", "alignment-baseline": "alignmentBaseline", "arabic-form": "arabicForm", "baseline-shift": "baselineShift", "cap-height": "capHeight", "clip-path": "clipPath", "clip-rule": "clipRule", "color-interpolation": "colorInterpolation", "color-interpolation-filters": "colorInterpolationFilters", "color-profile": "colorProfile", "color-rendering": "colorRendering", "dominant-baseline": "dominantBaseline", "enable-background": "enableBackground", "fill-opacity": "fillOpacity", "fill-rule": "fillRule", "flood-color": "floodColor", "flood-opacity": "floodOpacity", "font-family": "fontFamily", "font-size": "fontSize", "font-size-adjust": "fontSizeAdjust", "font-stretch": "fontStretch", "font-style": "fontStyle", "font-variant": "fontVariant", "font-weight": "fontWeight", "glyph-name": "glyphName", "glyph-orientation-horizontal": "glyphOrientationHorizontal", "glyph-orientation-vertical": "glyphOrientationVertical", "horiz-adv-x": "horizAdvX", "horiz-origin-x": "horizOriginX", "image-rendering": "imageRendering", "letter-spacing": "letterSpacing", "lighting-color": "lightingColor", "marker-end": "markerEnd", "marker-mid": "markerMid", "marker-start": "markerStart", "overline-position": "overlinePosition", "overline-thickness": "overlineThickness", "paint-order": "paintOrder", "panose-1": "panose1", "pointer-events": "pointerEvents", "rendering-intent": "renderingIntent", "shape-rendering": "shapeRendering", "stop-color": "stopColor", "stop-opacity": "stopOpacity", "strikethrough-position": "strikethroughPosition", "strikethrough-thickness": "strikethroughThickness", "stroke-dasharray": "strokeDasharray", "stroke-dashoffset": "strokeDashoffset", "stroke-linecap": "strokeLinecap", "stroke-linejoin": "strokeLinejoin", "stroke-miterlimit": "strokeMiterlimit", "stroke-opacity": "strokeOpacity", "stroke-width": "strokeWidth", "text-anchor": "textAnchor", "text-decoration": "textDecoration", "text-rendering": "textRendering", "underline-position": "underlinePosition", "underline-thickness": "underlineThickness", "unicode-bidi": "unicodeBidi", "unicode-range": "unicodeRange", "units-per-em": "unitsPerEm", "v-alphabetic": "vAlphabetic", "v-hanging": "vHanging", "v-ideographic": "vIdeographic", "v-mathematical": "vMathematical", "vector-effect": "vectorEffect", "vert-adv-y": "vertAdvY", "vert-origin-x": "vertOriginX", "vert-origin-y": "vertOriginY", "word-spacing": "wordSpacing", "writing-mode": "writingMode", "x-height": "xHeight", "xlink:actuate": "xlinkActuate", "xlink:arcrole": "xlinkArcrole", "xlink:href": "xlinkHref", "xlink:role": "xlinkRole", "xlink:show": "xlinkShow", "xlink:title": "xlinkTitle", "xlink:type": "xlinkType", "xml:base": "xmlBase", "xml:lang": "xmlLang", "xml:space": "xmlSpace" }; var DOM_PROPERTY_NAMES_ONE_WORD = [ // Global attributes - can be used on any HTML/DOM element // See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes "dir", "draggable", "hidden", "id", "lang", "nonce", "part", "slot", "style", "title", "translate", "inert", // Element specific attributes // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too) // To be considered if these should be added also to ATTRIBUTE_TAGS_MAP "accept", "action", "allow", "alt", "as", "async", "buffered", "capture", "challenge", "cite", "code", "cols", "content", "coords", "csp", "data", "decoding", "default", "defer", "disabled", "form", "headers", "height", "high", "href", "icon", "importance", "integrity", "kind", "label", "language", "loading", "list", "loop", "low", "manifest", "max", "media", "method", "min", "multiple", "muted", "name", "open", "optimum", "pattern", "ping", "placeholder", "poster", "preload", "profile", "rel", "required", "reversed", "role", "rows", "sandbox", "scope", "seamless", "selected", "shape", "size", "sizes", "span", "src", "start", "step", "summary", "target", "type", "value", "width", "wmode", "wrap", // SVG attributes // See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute "accumulate", "additive", "alphabetic", "amplitude", "ascent", "azimuth", "bbox", "begin", "bias", "by", "clip", "color", "cursor", "cx", "cy", "d", "decelerate", "descent", "direction", "display", "divisor", "dur", "dx", "dy", "elevation", "end", "exponent", "fill", "filter", "format", "from", "fr", "fx", "fy", "g1", "g2", "hanging", "height", "hreflang", "ideographic", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "local", "mask", "mode", "offset", "opacity", "operator", "order", "orient", "orientation", "origin", "overflow", "path", "ping", "points", "r", "radius", "rel", "restart", "result", "rotate", "rx", "ry", "scale", "seed", "slope", "spacing", "speed", "stemh", "stemv", "string", "stroke", "to", "transform", "u1", "u2", "unicode", "values", "version", "visibility", "widths", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", // OpenGraph meta tag attributes "property", // React specific attributes "ref", "key", "children", // Non-standard "results", "security", // Video specific "controls" ]; var DOM_PROPERTY_NAMES_TWO_WORDS = [ // Global attributes - can be used on any HTML/DOM element // See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes "accessKey", "autoCapitalize", "autoFocus", "contentEditable", "enterKeyHint", "exportParts", "inputMode", "itemID", "itemRef", "itemProp", "itemScope", "itemType", "spellCheck", "tabIndex", // Element specific attributes // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too) // To be considered if these should be added also to ATTRIBUTE_TAGS_MAP "acceptCharset", "autoComplete", "autoPlay", "border", "cellPadding", "cellSpacing", "classID", "codeBase", "colSpan", "contextMenu", "dateTime", "encType", "formAction", "formEncType", "formMethod", "formNoValidate", "formTarget", "frameBorder", "hrefLang", "httpEquiv", "imageSizes", "imageSrcSet", "isMap", "keyParams", "keyType", "marginHeight", "marginWidth", "maxLength", "mediaGroup", "minLength", "noValidate", "onAnimationEnd", "onAnimationIteration", "onAnimationStart", "onBlur", "onChange", "onClick", "onContextMenu", "onCopy", "onCompositionEnd", "onCompositionStart", "onCompositionUpdate", "onCut", "onDoubleClick", "onDrag", "onDragEnd", "onDragEnter", "onDragExit", "onDragLeave", "onError", "onFocus", "onInput", "onKeyDown", "onKeyPress", "onKeyUp", "onLoad", "onWheel", "onDragOver", "onDragStart", "onDrop", "onMouseDown", "onMouseEnter", "onMouseLeave", "onMouseMove", "onMouseOut", "onMouseOver", "onMouseUp", "onPaste", "onScroll", "onSelect", "onSubmit", "onToggle", "onTransitionEnd", "radioGroup", "readOnly", "referrerPolicy", "rowSpan", "srcDoc", "srcLang", "srcSet", "useMap", "fetchPriority", // SVG attributes // See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute "crossOrigin", "accentHeight", "alignmentBaseline", "arabicForm", "attributeName", "attributeType", "baseFrequency", "baselineShift", "baseProfile", "calcMode", "capHeight", "clipPathUnits", "clipPath", "clipRule", "colorInterpolation", "colorInterpolationFilters", "colorProfile", "colorRendering", "contentScriptType", "contentStyleType", "diffuseConstant", "dominantBaseline", "edgeMode", "enableBackground", "fillOpacity", "fillRule", "filterRes", "filterUnits", "floodColor", "floodOpacity", "fontFamily", "fontSize", "fontSizeAdjust", "fontStretch", "fontStyle", "fontVariant", "fontWeight", "glyphName", "glyphOrientationHorizontal", "glyphOrientationVertical", "glyphRef", "gradientTransform", "gradientUnits", "horizAdvX", "horizOriginX", "imageRendering", "kernelMatrix", "kernelUnitLength", "keyPoints", "keySplines", "keyTimes", "lengthAdjust", "letterSpacing", "lightingColor", "limitingConeAngle", "markerEnd", "markerMid", "markerStart", "markerHeight", "markerUnits", "markerWidth", "maskContentUnits", "maskUnits", "mathematical", "numOctaves", "overlinePosition", "overlineThickness", "panose1", "paintOrder", "pathLength", "patternContentUnits", "patternTransform", "patternUnits", "pointerEvents", "pointsAtX", "pointsAtY", "pointsAtZ", "preserveAlpha", "preserveAspectRatio", "primitiveUnits", "referrerPolicy", "refX", "refY", "rendering-intent", "repeatCount", "repeatDur", "requiredExtensions", "requiredFeatures", "shapeRendering", "specularConstant", "specularExponent", "spreadMethod", "startOffset", "stdDeviation", "stitchTiles", "stopColor", "stopOpacity", "strikethroughPosition", "strikethroughThickness", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin", "strokeMiterlimit", "strokeOpacity", "strokeWidth", "surfaceScale", "systemLanguage", "tableValues", "targetX", "targetY", "textAnchor", "textDecoration", "textRendering", "textLength", "transformOrigin", "underlinePosition", "underlineThickness", "unicodeBidi", "unicodeRange", "unitsPerEm", "vAlphabetic", "vHanging", "vIdeographic", "vMathematical", "vectorEffect", "vertAdvY", "vertOriginX", "vertOriginY", "viewBox", "viewTarget", "wordSpacing", "writingMode", "xHeight", "xChannelSelector", "xlinkActuate", "xlinkArcrole", "xlinkHref", "xlinkRole", "xlinkShow", "xlinkTitle", "xlinkType", "xmlBase", "xmlLang", "xmlnsXlink", "xmlSpace", "yChannelSelector", "zoomAndPan", // Safari/Apple specific, no listing available "autoCorrect", // https://stackoverflow.com/questions/47985384/html-autocorrect-for-text-input-is-not-working "autoSave", // https://stackoverflow.com/questions/25456396/what-is-autosave-attribute-supposed-to-do-how-do-i-use-it // React specific attributes https://reactjs.org/docs/dom-elements.html#differences-in-attributes "className", "dangerouslySetInnerHTML", "defaultValue", "defaultChecked", "htmlFor", // Events' capture events "onBeforeInput", "onChange", "onInvalid", "onReset", "onTouchCancel", "onTouchEnd", "onTouchMove", "onTouchStart", "suppressContentEditableWarning", "suppressHydrationWarning", "onAbort", "onCanPlay", "onCanPlayThrough", "onDurationChange", "onEmptied", "onEncrypted", "onEnded", "onLoadedData", "onLoadedMetadata", "onLoadStart", "onPause", "onPlay", "onPlaying", "onProgress", "onRateChange", "onResize", "onSeeked", "onSeeking", "onStalled", "onSuspend", "onTimeUpdate", "onVolumeChange", "onWaiting", "onCopyCapture", "onCutCapture", "onPasteCapture", "onCompositionEndCapture", "onCompositionStartCapture", "onCompositionUpdateCapture", "onFocusCapture", "onBlurCapture", "onChangeCapture", "onBeforeInputCapture", "onInputCapture", "onResetCapture", "onSubmitCapture", "onInvalidCapture", "onLoadCapture", "onErrorCapture", "onKeyDownCapture", "onKeyPressCapture", "onKeyUpCapture", "onAbortCapture", "onCanPlayCapture", "onCanPlayThroughCapture", "onDurationChangeCapture", "onEmptiedCapture", "onEncryptedCapture", "onEndedCapture", "onLoadedDataCapture", "onLoadedMetadataCapture", "onLoadStartCapture", "onPauseCapture", "onPlayCapture", "onPlayingCapture", "onProgressCapture", "onRateChangeCapture", "onSeekedCapture", "onSeekingCapture", "onStalledCapture", "onSuspendCapture", "onTimeUpdateCapture", "onVolumeChangeCapture", "onWaitingCapture", "onSelectCapture", "onTouchCancelCapture", "onTouchEndCapture", "onTouchMoveCapture", "onTouchStartCapture", "onScrollCapture", "onWheelCapture", "onAnimationEndCapture", "onAnimationIteration", "onAnimationStartCapture", "onTransitionEndCapture", "onAuxClick", "onAuxClickCapture", "onClickCapture", "onContextMenuCapture", "onDoubleClickCapture", "onDragCapture", "onDragEndCapture", "onDragEnterCapture", "onDragExitCapture", "onDragLeaveCapture", "onDragOverCapture", "onDragStartCapture", "onDropCapture", "onMouseDown", "onMouseDownCapture", "onMouseMoveCapture", "onMouseOutCapture", "onMouseOverCapture", "onMouseUpCapture", // Video specific "autoPictureInPicture", "controlsList", "disablePictureInPicture", "disableRemotePlayback" ]; var DOM_PROPERTIES_IGNORE_CASE = [ "charset", "allowFullScreen", "webkitAllowFullScreen", "mozAllowFullScreen", "webkitDirectory" ]; var ARIA_PROPERTIES = [ // See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes // Global attributes "aria-atomic", "aria-braillelabel", "aria-brailleroledescription", "aria-busy", "aria-controls", "aria-current", "aria-describedby", "aria-description", "aria-details", "aria-disabled", "aria-dropeffect", "aria-errormessage", "aria-flowto", "aria-grabbed", "aria-haspopup", "aria-hidden", "aria-invalid", "aria-keyshortcuts", "aria-label", "aria-labelledby", "aria-live", "aria-owns", "aria-relevant", "aria-roledescription", // Widget attributes "aria-autocomplete", "aria-checked", "aria-expanded", "aria-level", "aria-modal", "aria-multiline", "aria-multiselectable", "aria-orientation", "aria-placeholder", "aria-pressed", "aria-readonly", "aria-required", "aria-selected", "aria-sort", "aria-valuemax", "aria-valuemin", "aria-valuenow", "aria-valuetext", // Relationship attributes "aria-activedescendant", "aria-colcount", "aria-colindex", "aria-colindextext", "aria-colspan", "aria-posinset", "aria-rowcount", "aria-rowindex", "aria-rowindextext", "aria-rowspan", "aria-setsize" ]; var REACT_ON_PROPS = [ "onGotPointerCapture", "onGotPointerCaptureCapture", "onLostPointerCapture", "onLostPointerCapture", "onLostPointerCaptureCapture", "onPointerCancel", "onPointerCancelCapture", "onPointerDown", "onPointerDownCapture", "onPointerEnter", "onPointerEnterCapture", "onPointerLeave", "onPointerLeaveCapture", "onPointerMove", "onPointerMoveCapture", "onPointerOut", "onPointerOutCapture", "onPointerOver", "onPointerOverCapture", "onPointerUp", "onPointerUpCapture" ]; var POPOVER_API_PROPS = [ "popover", "popoverTarget", "popoverTargetAction", "onToggle", "onBeforeToggle" ]; function getDOMPropertyNames(context) { const ALL_DOM_PROPERTY_NAMES = DOM_PROPERTY_NAMES_TWO_WORDS.concat(DOM_PROPERTY_NAMES_ONE_WORD); if (testReactVersion(context, "<=", "16.1.0")) { ALL_DOM_PROPERTY_NAMES.push("allowTransparency"); return ALL_DOM_PROPERTY_NAMES; } if (testReactVersion(context, ">=", "16.4.0")) { ALL_DOM_PROPERTY_NAMES.push(...REACT_ON_PROPS); } testReactVersion(context, ">=", "19.0.0-rc.0") ? ALL_DOM_PROPERTY_NAMES.push(...POPOVER_API_PROPS) : ALL_DOM_PROPERTY_NAMES.push(...POPOVER_API_PROPS.map((prop) => prop.toLowerCase())); return ALL_DOM_PROPERTY_NAMES; } function isValidHTMLTagInJSX(childNode) { const tagConvention = /^[a-z][^-]*$/; if (tagConvention.test(childNode.parent.name.name)) { return !childNode.parent.attributes.some( (attrNode) => attrNode.type === "JSXAttribute" && attrNode.name.type === "JSXIdentifier" && attrNode.name.name === "is" // To learn more about custom web components and `is` attribute, // see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example ); } return false; } function normalizeAttributeCase(name3) { return DOM_PROPERTIES_IGNORE_CASE.find((element) => element.toLowerCase() === name3.toLowerCase()) || name3; } function isValidDataAttribute(name3) { return !/^data-xml/i.test(name3) && /^data-[^:]*$/.test(name3); } function hasUpperCaseCharacter(name3) { return name3.toLowerCase() !== name3; } function isValidAriaAttribute(name3) { return ARIA_PROPERTIES.some((element) => element === name3); } function getTagName(node) { if (node?.parent?.name) { return node.parent.name.name; } return null; } function tagNameHasDot(node) { return !!(node.parent?.name && node.parent.name.type === "JSXMemberExpression"); } function getStandardName(name3, context) { if (has(DOM_ATTRIBUTE_NAMES, name3)) { return DOM_ATTRIBUTE_NAMES[name3]; } if (has(SVGDOM_ATTRIBUTE_NAMES, name3)) { return SVGDOM_ATTRIBUTE_NAMES[name3]; } const names = getDOMPropertyNames(context); return names.find((element) => element.toLowerCase() === name3.toLowerCase()); } var messages = { dataLowercaseRequired: "React does not recognize data-* props with uppercase characters on a DOM element. Found '{{name}}', use '{{lowerCaseName}}' instead", invalidPropOnTag: "Invalid property '{{name}}' found on tag '{{tagName}}', but it is only allowed on: {{allowedTags}}", unknownProp: "Unknown property '{{name}}' found", unknownPropWithStandardName: "Unknown property '{{name}}' found, use '{{standardName}}' instead" }; var no_unknown_property_default = createRule({ meta: { type: "problem", docs: { description: "Disallow unknown `DOM` property." }, fixable: "code", messages, schema: [{ type: "object", additionalProperties: false, properties: { ignore: { type: "array", items: { type: "string" } }, requireDataLowercase: { type: "boolean", default: false } } }] }, name: RULE_NAME12, create: create12, defaultOptions: [] }); function create12(context) { const report = kit.Reporter.make(context); function getIgnoreConfig() { return context.options[0]?.ignore || DEFAULTS.ignore; } function getRequireDataLowercase() { return context.options[0] && typeof context.options[0].requireDataLowercase !== "undefined" ? !!context.options[0].requireDataLowercase : DEFAULTS.requireDataLowercase; } return { JSXAttribute(node) { const ignoreNames = getIgnoreConfig(); const actualName = getText(context, node.name); if (ignoreNames.indexOf(actualName) >= 0) { return; } const name3 = normalizeAttributeCase(actualName); if (tagNameHasDot(node)) { return; } if (isValidDataAttribute(name3)) { if (getRequireDataLowercase() && hasUpperCaseCharacter(name3)) { report.send({ node, messageId: "dataLowercaseRequired", data: { name: actualName, lowerCaseName: actualName.toLowerCase() } }); } return; } if (isValidAriaAttribute(name3)) return; const tagName = getTagName(node); if (tagName === "fbt" || tagName === "fbs") return; if (!isValidHTMLTagInJSX(node)) return; const allowedTags = has(ATTRIBUTE_TAGS_MAP, name3) ? ATTRIBUTE_TAGS_MAP[name3] : null; if (tagName && allowedTags) { if (allowedTags.indexOf(tagName) === -1) { report.send({ node, messageId: "invalidPropOnTag", data: { name: actualName, allowedTags: allowedTags.join(", "), tagName } }); } return; } const standardName = getStandardName(name3, context); const hasStandardNameButIsNotUsed = standardName && standardName !== name3; const usesStandardName = standardName && standardName === name3; if (usesStandardName) { return; } if (hasStandardNameButIsNotUsed) { report.send({ node, messageId: "unknownPropWithStandardName", data: { name: actualName, standardName }, fix(fixer) { return fixer.replaceText(node.name, standardName); } }); return; } report.send({ node, messageId: "unknownProp", data: { name: actualName } }); } }; } function has(obj, key) { return Object.hasOwn(obj, key); } function getText(context, node) { return context.sourceCode.getText(node); } function testReactVersion(context, comparator, version2) { const { version: localVersion } = shared.getSettingsFromContext(context); return compareVersions.compare(localVersion, version2, comparator); } var RULE_NAME13 = "no-unsafe-iframe-sandbox"; var RULE_FEATURES12 = []; var unsafeSandboxValues = [ ["allow-scripts", "allow-same-origin"] ]; function hasSafeSandbox(value) { if (typeof value !== "string") return false; return !unsafeSandboxValues.some((values) => { return values.every((v) => value.includes(v)); }); } var no_unsafe_iframe_sandbox_default = createRule({ meta: { type: "problem", docs: { description: "Enforces `sandbox` attribute for `iframe` elements is not set to unsafe combinations.", [Symbol.for("rule_features")]: RULE_FEATURES12 }, messages: { noUnsafeIframeSandbox: "Unsafe 'sandbox' attribute value on 'iframe' component." }, schema: [] }, name: RULE_NAME13, create: create13, defaultOptions: [] }); function create13(context) { const resolver = createJsxElementResolver(context); return { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "iframe") return; const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; const attributeNode = ER__namespace.getAttribute( context, propNameOnJsx, node.openingElement.attributes, context.sourceCode.getScope(node) ); if (attributeNode != null) { const attributeValue = ER__namespace.getAttributeValue( context, attributeNode, propNameOnJsx ); if (attributeValue.kind === "some" && !hasSafeSandbox(attributeValue.value)) { context.report({ messageId: "noUnsafeIframeSandbox", node: attributeNode }); return; } } if (customComponentProp?.defaultValue == null) return; if (!hasSafeSandbox(customComponentProp.defaultValue)) { context.report({ messageId: "noUnsafeIframeSandbox", node }); } } }; } var RULE_NAME14 = "no-unsafe-target-blank"; var RULE_FEATURES13 = []; function isExternalLinkLike(value) { if (value == null) return false; return value.startsWith("https://") || /^(?:\w+:|\/\/)/u.test(value); } function isSafeRel(value) { if (value == null) return false; return value === "noreferrer" || /\bnoreferrer\b/u.test(value); } var no_unsafe_target_blank_default = createRule({ meta: { type: "problem", docs: { description: 'Disallow `target="_blank"` without `rel="noreferrer noopener"`.', [Symbol.for("rule_features")]: RULE_FEATURES13 }, messages: { noUnsafeTargetBlank: `Using 'target="_blank"' on an external link without 'rel="noreferrer noopener"' is a security risk.` }, schema: [] }, name: RULE_NAME14, create: create14, defaultOptions: [] }); function create14(context) { const resolver = createJsxElementResolver(context); return { JSXElement(node) { const { attributes, domElementType } = resolver.resolve(node); if (domElementType !== "a") return; const elementScope = context.sourceCode.getScope(node); const getAttributeStringValue = (name3) => { const customComponentProp = findCustomComponentProp(name3, a