"use strict";
const require_runtime = require("../_virtual/_rolldown/runtime.js");
const require_index = require("../utils/index.js");
//#region lib/rules/no-mutating-props.js
/**
* @fileoverview disallow mutation component props
* @author 2018 Armano
*/
var require_no_mutating_props = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
	/**
	* @typedef {{name?: string, set: Set<string>}} PropsInfo
	*/
	const utils = require_index.default;
	const { findVariable } = require("@eslint-community/eslint-utils");
	const GLOBALS_WHITE_LISTED = /* @__PURE__ */ new Set([
		"Infinity",
		"undefined",
		"NaN",
		"isFinite",
		"isNaN",
		"parseFloat",
		"parseInt",
		"decodeURI",
		"decodeURIComponent",
		"encodeURI",
		"encodeURIComponent",
		"Math",
		"Number",
		"Date",
		"Array",
		"Object",
		"Boolean",
		"String",
		"RegExp",
		"Map",
		"Set",
		"JSON",
		"Intl",
		"BigInt"
	]);
	/**
	* @param {ASTNode} node
	* @returns {VExpressionContainer}
	*/
	function getVExpressionContainer(node) {
		let n = node;
		while (n.type !== "VExpressionContainer") n = n.parent;
		return n;
	}
	/**
	* @param {ASTNode} node
	* @returns {node is Identifier}
	*/
	function isVmReference(node) {
		if (node.type !== "Identifier") return false;
		const parent = node.parent;
		if (parent.type === "MemberExpression") {
			if (parent.property === node) return false;
		} else if (parent.type === "Property" && parent.key === node && !parent.computed) return false;
		const exprContainer = getVExpressionContainer(node);
		for (const reference of exprContainer.references) {
			if (reference.variable != null) continue;
			if (reference.id === node) return true;
		}
		return false;
	}
	/**
	* @param { object } options
	* @param { boolean } options.shallowOnly Enables mutating the value of a prop but leaving the reference the same
	*/
	function parseOptions(options) {
		return Object.assign({ shallowOnly: false }, options);
	}
	module.exports = {
		meta: {
			type: "suggestion",
			docs: {
				description: "disallow mutation of component props",
				categories: ["vue3-essential", "vue2-essential"],
				url: "https://eslint.vuejs.org/rules/no-mutating-props.html"
			},
			fixable: null,
			schema: [{
				type: "object",
				properties: { shallowOnly: { type: "boolean" } },
				additionalProperties: false
			}],
			messages: { unexpectedMutation: "Unexpected mutation of \"{{key}}\" prop." }
		},
		/** @param {RuleContext} context */
		create(context) {
			const { shallowOnly } = parseOptions(context.options[0]);
			/** @type {Map<ObjectExpression|CallExpression, PropsInfo>} */
			const propsMap = /* @__PURE__ */ new Map();
			/** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | { type: 'setup', object: CallExpression } | null } */
			let vueObjectData = null;
			/**
			* @param {ASTNode} node
			* @param {string} name
			*/
			function report(node, name) {
				context.report({
					node,
					messageId: "unexpectedMutation",
					data: { key: name }
				});
			}
			/**
			* @param {MemberExpression|AssignmentProperty} node
			* @returns {string}
			*/
			function getPropertyNameText(node) {
				const name = utils.getStaticPropertyName(node);
				if (name) return name;
				if (node.computed) {
					const expr = node.type === "Property" ? node.key : node.property;
					return `[${context.sourceCode.getText(expr)}]`;
				}
				return "?unknown?";
			}
			/**
			* @param {MemberExpression|Identifier} props
			* @param {string} name
			* @param {boolean} isRootProps
			*/
			function verifyMutating(props, name, isRootProps = false) {
				const invalid = utils.findMutating(props);
				if (invalid && isShallowOnlyInvalid(invalid, isRootProps)) report(invalid.node, name);
			}
			/**
			* @param {Pattern} param
			* @param {string[]} path
			* @returns {Generator<{ node: Identifier, path: string[] }>}
			*/
			function* iteratePatternProperties(param, path) {
				if (!param) return;
				switch (param.type) {
					case "Identifier":
						yield {
							node: param,
							path
						};
						break;
					case "RestElement":
						yield* iteratePatternProperties(param.argument, path);
						break;
					case "AssignmentPattern":
						yield* iteratePatternProperties(param.left, path);
						break;
					case "ObjectPattern":
						for (const prop of param.properties) if (prop.type === "Property") {
							const name = getPropertyNameText(prop);
							yield* iteratePatternProperties(prop.value, [...path, name]);
						} else if (prop.type === "RestElement") yield* iteratePatternProperties(prop.argument, path);
						break;
					case "ArrayPattern":
						for (let index = 0; index < param.elements.length; index++) {
							const element = param.elements[index];
							if (element) yield* iteratePatternProperties(element, [...path, String(index)]);
						}
						break;
				}
			}
			/**
			* @param {Identifier} prop
			* @param {string[]} path
			*/
			function verifyPropVariable(prop, path) {
				const variable = findVariable(utils.getScope(context, prop), prop);
				if (!variable) return;
				for (const reference of variable.references) {
					if (!reference.isRead()) continue;
					const id = reference.identifier;
					const invalid = utils.findMutating(id);
					if (!invalid || !isShallowOnlyInvalid(invalid, path.length === 0)) continue;
					let name;
					if (path.length === 0) {
						if (invalid.pathNodes.length === 0) continue;
						const mem = invalid.pathNodes[0];
						name = getPropertyNameText(mem);
					} else {
						if (invalid.pathNodes.length === 0 && invalid.kind !== "call") continue;
						name = path[0];
					}
					report(invalid.node, name);
				}
			}
			function* extractDefineVariableNames() {
				const globalScope = context.sourceCode.scopeManager.globalScope;
				if (globalScope) {
					for (const variable of globalScope.variables) if (variable.defs.length > 0) yield variable.name;
					const moduleScope = globalScope.childScopes.find((scope) => scope.type === "module");
					const moduleVariables = moduleScope && moduleScope.variables || [];
					for (const variable of moduleVariables) if (variable.defs.length > 0) yield variable.name;
				}
			}
			/**
			* Is shallowOnly false or the prop reassigned
			* @param {Exclude<ReturnType<typeof utils.findMutating>, null>} invalid
			* @param {boolean} isRootProps
			* @return {boolean}
			*/
			function isShallowOnlyInvalid(invalid, isRootProps) {
				return !shallowOnly || invalid.pathNodes.length === (isRootProps ? 1 : 0) && ["assignment", "update"].includes(invalid.kind);
			}
			return utils.compositingVisitors({}, utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) {
				const defineVariableNames = new Set(extractDefineVariableNames());
				const propsInfo = {
					name: "",
					set: new Set(props.map((p) => p.propName).filter(
						/**
						* @returns {propName is string}
						*/
						(propName) => utils.isDef(propName) && !GLOBALS_WHITE_LISTED.has(propName) && !defineVariableNames.has(propName)
					))
				};
				propsMap.set(node, propsInfo);
				vueObjectData = {
					type: "setup",
					object: node
				};
				let target = node;
				if (target.parent && target.parent.type === "CallExpression" && target.parent.arguments[0] === target && target.parent.callee.type === "Identifier" && target.parent.callee.name === "withDefaults") target = target.parent;
				if (!target.parent || target.parent.type !== "VariableDeclarator" || target.parent.init !== target) return;
				const patternProperties = iteratePatternProperties(target.parent.id, []);
				for (const { node: prop, path } of patternProperties) {
					if (path.length === 0) propsInfo.name = prop.name;
					else propsInfo.set.add(prop.name);
					verifyPropVariable(prop, path);
				}
			} }), utils.defineVueVisitor(context, {
				onVueObjectEnter(node) {
					propsMap.set(node, { set: new Set(utils.getComponentPropsFromOptions(node).map((p) => p.propName).filter(utils.isDef)) });
				},
				onVueObjectExit(node, { type }) {
					if ((!vueObjectData || vueObjectData.type !== "export" && vueObjectData.type !== "setup") && type !== "instance") vueObjectData = {
						type,
						object: node
					};
				},
				onSetupFunctionEnter(node) {
					const propsParam = node.params[0];
					if (!propsParam) return;
					if (propsParam.type === "RestElement" || propsParam.type === "ArrayPattern") return;
					const patternProperties = iteratePatternProperties(propsParam, []);
					for (const { node: prop, path } of patternProperties) verifyPropVariable(prop, path);
				},
				/** @param {(Identifier | ThisExpression) & { parent: MemberExpression } } node */
				"MemberExpression > :matches(Identifier, ThisExpression)"(node, { node: vueNode }) {
					if (!utils.isThis(node, context)) return;
					const mem = node.parent;
					if (mem.object !== node) return;
					const name = utils.getStaticPropertyName(mem);
					if (name && propsMap.get(vueNode).set.has(name)) verifyMutating(mem, name);
				}
			}), utils.defineTemplateBodyVisitor(context, {
				/** @param {ThisExpression & { parent: MemberExpression } } node */
				"VExpressionContainer MemberExpression > ThisExpression"(node) {
					if (!vueObjectData) return;
					const mem = node.parent;
					if (mem.object !== node) return;
					const name = utils.getStaticPropertyName(mem);
					if (name && propsMap.get(vueObjectData.object).set.has(name)) verifyMutating(mem, name);
				},
				/** @param {Identifier } node */
				"VExpressionContainer Identifier"(node) {
					if (!vueObjectData) return;
					if (!isVmReference(node)) return;
					const propsInfo = propsMap.get(vueObjectData.object);
					const isRootProps = !!node.name && propsInfo.name === node.name;
					const parent = node.parent;
					const name = isRootProps && parent.type === "MemberExpression" && utils.getStaticPropertyName(parent) || node.name;
					if (name && (propsInfo.set.has(name) || isRootProps)) verifyMutating(node, name, isRootProps);
				},
				/** @param {ESNode} node */
				"VAttribute[directive=true]:matches([key.name.name='model'], [key.name.name='bind']) VExpressionContainer > *"(node) {
					if (!vueObjectData) return;
					let attr = node.parent;
					while (attr && attr.type !== "VAttribute") attr = attr.parent;
					if (attr && attr.directive && attr.key.name.name === "bind" && attr.key.modifiers.every((mod) => mod.name !== "sync")) return;
					const propsInfo = propsMap.get(vueObjectData.object);
					const nodes = utils.getMemberChaining(node);
					const first = nodes[0];
					let name;
					if (isVmReference(first)) if (first.name === propsInfo.name) {
						if (shallowOnly && nodes.length > 2) return;
						name = nodes[1] && getPropertyNameText(nodes[1]) || first.name;
					} else {
						if (shallowOnly && nodes.length > 1) return;
						name = first.name;
						if (!name || !propsInfo.set.has(name)) return;
					}
					else if (first.type === "ThisExpression") {
						if (shallowOnly && nodes.length > 2) return;
						const mem = nodes[1];
						if (!mem) return;
						name = utils.getStaticPropertyName(mem);
						if (!name || !propsInfo.set.has(name)) return;
					} else return;
					report(node, name);
				}
			}));
		}
	};
}));
//#endregion
Object.defineProperty(exports, "default", {
	enumerable: true,
	get: function() {
		return require_no_mutating_props();
	}
});
