eslint-plugin-react-dom
Version:
ESLint React's ESLint plugin for React DOM related rules.
1,816 lines (1,807 loc) • 48.9 kB
JavaScript
import { createRuleForPlugin, getSettingsFromContext, RE_JAVASCRIPT_PROTOCOL, DEFAULT_ESLINT_REACT_SETTINGS } from '@eslint-react/shared';
import * as JSX from '@eslint-react/jsx';
import { AST_NODE_TYPES } from '@typescript-eslint/types';
import * as AST from '@eslint-react/ast';
import { compare } from 'compare-versions';
import { _ } from '@eslint-react/eff';
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-missing-button-type": "warn",
"react-dom/no-missing-iframe-sandbox": "warn",
"react-dom/no-namespace": "error",
"react-dom/no-render-return-value": "error",
"react-dom/no-script-url": "warn",
"react-dom/no-unknown-property": "warn",
"react-dom/no-unsafe-iframe-sandbox": "warn",
"react-dom/no-unsafe-target-blank": "warn",
"react-dom/no-void-elements-with-children": "warn"
};
var settings = {
"react-x": DEFAULT_ESLINT_REACT_SETTINGS
};
// package.json
var name2 = "eslint-plugin-react-dom";
var version = "1.32.0";
var createRule = createRuleForPlugin("dom");
// src/utils/find-custom-component.ts
function findCustomComponent(name3, components) {
return components.findLast((c) => c.name === name3 || c.re.test(name3));
}
function findCustomComponentProp(name3, props) {
return props.findLast((a) => a.as === name3);
}
function getElementTypeOnJsxAndDom(context, node, polymorphicPropName, additionalComponents = []) {
const name3 = JSX.getElementType(node);
if (name3 === name3.toLowerCase()) return [name3, name3];
const component = additionalComponents.findLast((c) => c.name === name3 || c.re.test(name3));
if (component != null) return [name3, component.as];
if (polymorphicPropName == null) return [name3, name3];
const initialScope = context.sourceCode.getScope(node);
const attributeNode = JSX.getAttribute(
polymorphicPropName,
node.openingElement.attributes,
initialScope
);
if (attributeNode == null) return [name3, name3];
const polymorphicPropValue = JSX.getAttributeValue(attributeNode, polymorphicPropName, initialScope);
if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") {
return [name3, polymorphicPropValue.value];
}
return [name3, name3];
}
// src/rules/no-dangerously-set-innerhtml.ts
var RULE_NAME = "no-dangerously-set-innerhtml";
var RULE_FEATURES = [
"CHK"
];
var no_dangerously_set_innerhtml_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow when a DOM component is using 'dangerouslySetInnerHTML'",
[Symbol.for("rule_features")]: RULE_FEATURES
},
messages: {
noDangerouslySetInnerhtml: "Using 'dangerouslySetInnerHTML' may have security implications."
},
schema: []
},
name: RULE_NAME,
create(context) {
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
return {
JSXElement(node) {
const attributes = node.openingElement.attributes;
const attribute = JSX.getAttribute(
"dangerouslySetInnerHTML",
attributes,
context.sourceCode.getScope(node)
);
if (attribute == null) return;
context.report({
messageId: "noDangerouslySetInnerhtml",
node: attribute
});
}
};
},
defaultOptions: []
});
var RULE_NAME2 = "no-dangerously-set-innerhtml-with-children";
var RULE_FEATURES2 = [
"CHK"
];
var no_dangerously_set_innerhtml_with_children_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow when a DOM component is using both 'children' and 'dangerouslySetInnerHTML'",
[Symbol.for("rule_features")]: RULE_FEATURES2
},
messages: {
noDangerouslySetInnerhtmlWithChildren: "A DOM component cannot use both 'children' and 'dangerouslySetInnerHTML'."
},
schema: []
},
name: RULE_NAME2,
create(context) {
if (!context.sourceCode.text.includes("dangerouslySetInnerHTML")) return {};
return {
JSXElement(node) {
const attributes = node.openingElement.attributes;
const initialScope = context.sourceCode.getScope(node);
const hasChildren = hasChildrenWithin(node) || JSX.hasAttribute("children", attributes, initialScope);
if (hasChildren && JSX.hasAttribute("dangerouslySetInnerHTML", attributes, initialScope)) {
context.report({
messageId: "noDangerouslySetInnerhtmlWithChildren",
node
});
}
}
};
},
defaultOptions: []
});
function hasChildrenWithin(node) {
return node.children.length > 0 && node.children[0] != null && !JSX.isLineBreak(node.children[0]);
}
var RULE_NAME3 = "no-find-dom-node";
var RULE_FEATURES3 = [
"CHK"
];
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(context) {
if (!context.sourceCode.text.includes("findDOMNode")) return {};
return {
CallExpression(node) {
const { callee } = node;
switch (callee.type) {
case AST_NODE_TYPES.Identifier:
if (callee.name === "findDOMNode") {
context.report({ messageId: "noFindDomNode", node });
}
return;
case AST_NODE_TYPES.MemberExpression:
if (callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "findDOMNode") {
context.report({ messageId: "noFindDomNode", node });
}
return;
}
}
};
},
defaultOptions: []
});
var RULE_NAME4 = "no-flush-sync";
var RULE_FEATURES4 = [
"CHK"
];
var no_flush_sync_default = createRule({
meta: {
type: "problem",
docs: {
description: "warns against using `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(context) {
if (!context.sourceCode.text.includes("flushSync")) return {};
return {
CallExpression(node) {
const { callee } = node;
switch (callee.type) {
case AST_NODE_TYPES.Identifier:
if (callee.name === "flushSync") {
context.report({ messageId: "noFlushSync", node });
}
return;
case AST_NODE_TYPES.MemberExpression:
if (callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "flushSync") {
context.report({ messageId: "noFlushSync", node });
}
return;
}
}
};
},
defaultOptions: []
});
var RULE_NAME5 = "no-missing-button-type";
var RULE_FEATURES5 = [
"CHK"
];
var no_missing_button_type_default = createRule({
meta: {
type: "problem",
docs: {
description: "enforce that button component have an explicit 'type' attribute",
[Symbol.for("rule_features")]: RULE_FEATURES5
},
messages: {
noMissingButtonType: "Add missing 'type' attribute on 'button' component."
},
schema: []
},
name: RULE_NAME5,
create(context) {
const settings2 = getSettingsFromContext(context);
const polymorphicPropName = settings2.polymorphicPropName;
const additionalComponents = settings2.additionalComponents.filter((c) => c.as === "button");
return {
JSXElement(node) {
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
context,
node,
polymorphicPropName,
additionalComponents
);
if (elementNameOnDom !== "button") return;
const elementScope = context.sourceCode.getScope(node);
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
const customComponentProp = findCustomComponentProp("type", customComponent?.attributes ?? []);
const propNameOnJsx = customComponentProp?.name ?? "type";
const attributeNode = JSX.getAttribute(
propNameOnJsx,
node.openingElement.attributes,
elementScope
);
if (attributeNode != null) {
const attributeScope = context.sourceCode.getScope(attributeNode);
const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope);
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
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME6 = "no-missing-iframe-sandbox";
var RULE_FEATURES6 = [
"CHK"
];
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: "enforce that 'iframe' component have an explicit 'sandbox' attribute",
[Symbol.for("rule_features")]: RULE_FEATURES6
},
messages: {
noMissingIframeSandbox: "Add missing 'sandbox' attribute on 'iframe' component."
},
schema: []
},
name: RULE_NAME6,
create(context) {
const settings2 = getSettingsFromContext(context);
const polymorphicPropName = settings2.polymorphicPropName;
const additionalComponents = settings2.additionalComponents.filter((c) => c.as === "iframe");
return {
JSXElement(node) {
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
context,
node,
polymorphicPropName,
additionalComponents
);
if (elementNameOnDom !== "iframe") return;
const elementScope = context.sourceCode.getScope(node);
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
const attributeNode = JSX.getAttribute(
propNameOnJsx,
node.openingElement.attributes,
elementScope
);
if (attributeNode != null) {
const attributeScope = context.sourceCode.getScope(attributeNode);
const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope);
if (attributeValue.kind === "some" && hasValidSandBox(attributeValue.value)) return;
context.report({
messageId: "noMissingIframeSandbox",
node: attributeNode
});
return;
}
if (!hasValidSandBox(customComponentProp?.defaultValue)) {
context.report({
messageId: "noMissingIframeSandbox",
node
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME7 = "no-namespace";
var RULE_FEATURES7 = [
"CHK"
];
var no_namespace_default = createRule({
meta: {
type: "problem",
docs: {
description: "enforce that namespaces are not used in React elements",
[Symbol.for("rule_features")]: RULE_FEATURES7
},
messages: {
noNamespace: "A React component '{{name}}' must not be in a namespace, as React does not support them."
},
schema: []
},
name: RULE_NAME7,
create(context) {
return {
JSXElement(node) {
const name3 = JSX.getElementType(node);
if (typeof name3 !== "string" || !name3.includes(":")) {
return;
}
context.report({
messageId: "noNamespace",
node: node.openingElement.name,
data: {
name: name3
}
});
}
};
},
defaultOptions: []
});
var RULE_NAME8 = "no-render-return-value";
var RULE_FEATURES8 = [
"CHK"
];
var banParentTypes = [
AST_NODE_TYPES.VariableDeclarator,
AST_NODE_TYPES.Property,
AST_NODE_TYPES.ReturnStatement,
AST_NODE_TYPES.ArrowFunctionExpression,
AST_NODE_TYPES.AssignmentExpression
];
var no_render_return_value_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow usage of the return value of 'ReactDOM.render'",
[Symbol.for("rule_features")]: RULE_FEATURES8
},
messages: {
noRenderReturnValue: "Do not depend on the return value from '{{objectName}}.render'."
},
schema: []
},
name: RULE_NAME8,
create(context) {
return {
CallExpression(node) {
const { callee, parent } = node;
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
return;
}
if (callee.object.type !== AST_NODE_TYPES.Identifier) {
return;
}
if (!("name" in callee.object)) {
return;
}
const objectName = callee.object.name;
if (objectName.toLowerCase() !== "reactdom" || callee.property.type !== AST_NODE_TYPES.Identifier || callee.property.name !== "render" || !AST.isOneOf(banParentTypes)(parent)) {
return;
}
context.report({
messageId: "noRenderReturnValue",
node,
data: {
objectName
}
});
}
};
},
defaultOptions: []
});
var RULE_NAME9 = "no-script-url";
var RULE_FEATURES9 = [
"CHK"
];
var no_script_url_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow 'javascript:' URLs as JSX event handler prop's value",
[Symbol.for("rule_features")]: RULE_FEATURES9
},
messages: {
noScriptUrl: "Using a `javascript:` URL is a security risk and should be avoided."
},
schema: []
},
name: RULE_NAME9,
create(context) {
return {
JSXAttribute(node) {
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier || node.value == null) {
return;
}
const attributeScope = context.sourceCode.getScope(node);
const attributeName = JSX.getAttributeName(node);
const attributeValue = JSX.getAttributeValue(node, attributeName, attributeScope);
if (attributeValue.kind === "none" || typeof attributeValue.value !== "string") return;
if (RE_JAVASCRIPT_PROTOCOL.test(attributeValue.value)) {
context.report({
messageId: "noScriptUrl",
node: node.value
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME10 = "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: {
docs: {
description: "disallow usage of 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_NAME10,
create(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(context, messages.dataLowercaseRequired, "dataLowercaseRequired", {
node,
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(context, messages.invalidPropOnTag, "invalidPropOnTag", {
node,
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(context, messages.unknownPropWithStandardName, "unknownPropWithStandardName", {
node,
data: {
name: actualName,
standardName
},
fix(fixer) {
return fixer.replaceText(node.name, standardName);
}
});
return;
}
report(context, messages.unknownProp, "unknownProp", {
node,
data: {
name: actualName
}
});
}
};
},
defaultOptions: []
});
function has(obj, key) {
return Object.hasOwn(obj, key);
}
function getText(context, node) {
return context.sourceCode.getText(node);
}
function report(context, message, messageId, data) {
context.report({
messageId,
...data
});
}
function testReactVersion(context, comparator, version2) {
const { version: localVersion } = getSettingsFromContext(context);
return compare(localVersion, version2, comparator);
}
var RULE_NAME11 = "no-unsafe-iframe-sandbox";
var RULE_FEATURES10 = [
"CHK"
];
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: "disallow unsafe iframe 'sandbox' attribute combinations",
[Symbol.for("rule_features")]: RULE_FEATURES10
},
messages: {
noUnsafeIframeSandbox: "Unsafe 'sandbox' attribute value on 'iframe' component."
},
schema: []
},
name: RULE_NAME11,
create(context) {
const settings2 = getSettingsFromContext(context);
const polymorphicPropName = settings2.polymorphicPropName;
const additionalComponents = settings2.additionalComponents.filter((c) => c.as === "iframe");
return {
JSXElement(node) {
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
context,
node,
polymorphicPropName,
additionalComponents
);
if (elementNameOnDom !== "iframe") return;
const elementScope = context.sourceCode.getScope(node);
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
const attributeNode = JSX.getAttribute(
propNameOnJsx,
node.openingElement.attributes,
elementScope
);
if (attributeNode != null) {
const attributeScope = context.sourceCode.getScope(attributeNode);
const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope);
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
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME12 = "no-unsafe-target-blank";
var RULE_FEATURES11 = [
"CHK"
];
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"' on an external link without 'rel="noreferrer noopener"'`,
[Symbol.for("rule_features")]: RULE_FEATURES11
},
messages: {
noUnsafeTargetBlank: `Using 'target="_blank"' on an external link without 'rel="noreferrer noopener"' is a security risk.`
},
schema: []
},
name: RULE_NAME12,
create(context) {
const settings2 = getSettingsFromContext(context);
const polymorphicPropName = settings2.polymorphicPropName;
const additionalComponents = settings2.additionalComponents.filter((c) => c.as === "a");
return {
JSXElement(node) {
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
context,
node,
polymorphicPropName,
additionalComponents
);
if (elementNameOnDom !== "a") return;
const elementScope = context.sourceCode.getScope(node);
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
const getAttributeStringValue = (name3) => {
const customComponentProp = findCustomComponentProp(name3, customComponent?.attributes ?? []);
const propNameOnJsx = customComponentProp?.name ?? name3;
const attributeNode = JSX.getAttribute(
propNameOnJsx,
node.openingElement.attributes,
elementScope
);
if (attributeNode == null) return customComponentProp?.defaultValue;
const attributeScope = context.sourceCode.getScope(attributeNode);
const attributeValue = JSX.getAttributeValue(attributeNode, propNameOnJsx, attributeScope);
if (attributeValue.kind === "some" && typeof attributeValue.value === "string") {
return attributeValue.value;
}
return _;
};
if (getAttributeStringValue("target") !== "_blank") {
return;
}
if (!isExternalLinkLike(getAttributeStringValue("href"))) {
return;
}
if (isSafeRel(getAttributeStringValue("rel"))) {
return;
}
context.report({
messageId: "noUnsafeTargetBlank",
node
});
}
};
},
defaultOptions: []
});
var RULE_NAME13 = "no-void-elements-with-children";
var RULE_FEATURES12 = [
"CHK"
];
var voidElements = /* @__PURE__ */ new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"menuitem",
"meta",
"param",
"source",
"track",
"wbr"
]);
var no_void_elements_with_children_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow void elements (AKA self-closing elements) from having children",
[Symbol.for("rule_features")]: RULE_FEATURES12
},
messages: {
noVoidElementsWithChildren: "'{{element}}' is a void element tag and must not have children."
},
schema: []
},
name: RULE_NAME13,
create(context) {
return {
JSXElement(node) {
const elementName = JSX.getElementType(node);
if (elementName.length === 0 || !voidElements.has(elementName)) {
return;
}
if (node.children.length > 0) {
context.report({
messageId: "noVoidElementsWithChildren",
node,
data: {
element: elementName
}
});
}
const { attributes } = node.openingElement;
const initialScope = context.sourceCode.getScope(node);
const hasAttribute3 = (name3) => JSX.hasAttribute(name3, attributes, initialScope);
if (hasAttribute3("children") || hasAttribute3("dangerouslySetInnerHTML")) {
context.report({
messageId: "noVoidElementsWithChildren",
node,
data: {
element: elementName
}
});
}
}
};
},
defaultOptions: []
});
// src/plugin.ts
var plugin = {
meta: {
name: name2,
version
},
rules: {
"no-dangerously-set-innerhtml": no_dangerously_set_innerhtml_default,
"no-dangerously-set-innerhtml-with-children": no_dangerously_set_innerhtml_with_children_default,
"no-find-dom-node": no_find_dom_node_default,
"no-flush-sync": no_flush_sync_default,
"no-missing-button-type": no_missing_button_type_default,
"no-missing-iframe-sandbox": no_missing_iframe_sandbox_default,
"no-namespace": no_namespace_default,
"no-render-return-value": no_render_return_value_default,
"no-script-url": no_script_url_default,
"no-unknown-property": no_unknown_property_default,
"no-unsafe-iframe-sandbox": no_unsafe_iframe_sandbox_default,
"no-unsafe-target-blank": no_unsafe_target_blank_default,
"no-void-elements-with-children": no_void_elements_with_children_default,
// Part: deprecated rules
/** @deprecated Use `no-void-elements-with-children` instead */
"no-children-in-void-dom-elements": no_void_elements_with_children_default
}
};
// src/index.ts
function makeConfig(config) {
return {
...config,
plugins: {
"react-dom": plugin
}
};
}
function makeLegacyConfig({ rules: rules2 }) {
return {
plugins: ["react-dom"],
rules: rules2
};
}
var index_default = {
...plugin,
configs: {
["recommended"]: makeConfig(recommended_exports),
["recommended-legacy"]: makeLegacyConfig(recommended_exports)
}
};
export { index_default as default };