pasika 0.10.29 → 0.10.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3984,6 +3984,58 @@ function looksLikeUserFacingString(value) {
3984
3984
  function looksLikeUserFacingJsxText(value) {
3985
3985
  return typeof value === "string" && /\p{L}/u.test(value);
3986
3986
  }
3987
+ var USER_FACING_PROPERTY_NAMES = /* @__PURE__ */ new Set([
3988
+ "alt",
3989
+ "aria-label",
3990
+ "cancelText",
3991
+ "children",
3992
+ "confirmText",
3993
+ "description",
3994
+ "emptyText",
3995
+ "errorMessage",
3996
+ "helperText",
3997
+ "label",
3998
+ "placeholder",
3999
+ "submitText",
4000
+ "successMessage",
4001
+ "title"
4002
+ ]);
4003
+ var USER_FACING_VARIABLE_SUFFIX_RE = /(?:Description|Label|Message|Text|Title)$/u;
4004
+ var TOAST_METHOD_NAMES = /* @__PURE__ */ new Set(["error", "info", "success", "warning"]);
4005
+ function getNodeName(node) {
4006
+ if (!node) return null;
4007
+ if (node.type === "Identifier") return node.name;
4008
+ if (node.type === "Literal" && typeof node.value === "string") return node.value;
4009
+ return null;
4010
+ }
4011
+ function containsUserFacingString(node) {
4012
+ if (!node) return false;
4013
+ if (node.type === "Literal") return looksLikeUserFacingJsxText(node.value);
4014
+ if (node.type === "TemplateLiteral") {
4015
+ return node.quasis.some((quasi) => looksLikeUserFacingJsxText(quasi.value.cooked ?? quasi.value.raw));
4016
+ }
4017
+ if (node.type === "ConditionalExpression") {
4018
+ return containsUserFacingString(node.consequent) || containsUserFacingString(node.alternate);
4019
+ }
4020
+ if (node.type === "LogicalExpression") {
4021
+ return containsUserFacingString(node.left) || containsUserFacingString(node.right);
4022
+ }
4023
+ if (node.type === "BinaryExpression") {
4024
+ return node.operator === "+" && (containsUserFacingString(node.left) || containsUserFacingString(node.right));
4025
+ }
4026
+ return false;
4027
+ }
4028
+ function isUserFacingVariable(node) {
4029
+ const name = getNodeName(node);
4030
+ return name !== null && USER_FACING_VARIABLE_SUFFIX_RE.test(name);
4031
+ }
4032
+ function objectContainsUserFacingProperties(node) {
4033
+ return node.properties.some((property) => {
4034
+ if (property.type !== "Property") return false;
4035
+ const name = getNodeName(property.key);
4036
+ return name !== null && USER_FACING_PROPERTY_NAMES.has(name) && containsUserFacingString(property.value);
4037
+ });
4038
+ }
3987
4039
  var localesLocationRule = {
3988
4040
  meta: {
3989
4041
  schema: [],
@@ -4008,7 +4060,42 @@ var localesLocationRule = {
4008
4060
  message: "User-facing JSX text must come from src/locales/index.ts, not be written inline."
4009
4061
  });
4010
4062
  },
4063
+ JSXAttribute(node) {
4064
+ const name = node.name?.name;
4065
+ if (typeof name !== "string" || !USER_FACING_PROPERTY_NAMES.has(name)) return;
4066
+ const value = node.value;
4067
+ if (!value) return;
4068
+ const hasUserFacingString = value.type === "JSXExpressionContainer" ? containsUserFacingString(value.expression) : looksLikeUserFacingJsxText(value.value);
4069
+ if (!hasUserFacingString) return;
4070
+ context.report({
4071
+ node,
4072
+ message: "User-facing JSX attribute text must come from src/locales/index.ts, not be written inline."
4073
+ });
4074
+ },
4075
+ "JSXElement > JSXExpressionContainer, JSXFragment > JSXExpressionContainer"(node) {
4076
+ if (!containsUserFacingString(node.expression)) return;
4077
+ context.report({
4078
+ node,
4079
+ message: "User-facing JSX expression text must come from src/locales/index.ts, not be written inline."
4080
+ });
4081
+ },
4082
+ ObjectExpression(node) {
4083
+ if (skipObjectLiteralCheck || node.parent.type === "VariableDeclarator" || !objectContainsUserFacingProperties(node)) {
4084
+ return;
4085
+ }
4086
+ context.report({
4087
+ node,
4088
+ message: "User-facing object properties must come from src/locales/index.ts, not be written inline."
4089
+ });
4090
+ },
4011
4091
  VariableDeclarator(node) {
4092
+ if (node.id.type === "Identifier" && isUserFacingVariable(node.id) && containsUserFacingString(node.init)) {
4093
+ context.report({
4094
+ node,
4095
+ message: "User-facing variable text must come from src/locales/index.ts, not be written inline."
4096
+ });
4097
+ return;
4098
+ }
4012
4099
  if (skipObjectLiteralCheck) return;
4013
4100
  if (node.id.type === "Identifier" && node.init?.type === "ObjectExpression" && node.init.properties.length > 0 && looksLikeLocaleKey(node.id.name)) {
4014
4101
  const hasUserFacingStringValues = node.init.properties.some(
@@ -4021,6 +4108,24 @@ var localesLocationRule = {
4021
4108
  });
4022
4109
  }
4023
4110
  }
4111
+ },
4112
+ AssignmentExpression(node) {
4113
+ if (!isUserFacingVariable(node.left) || !containsUserFacingString(node.right)) return;
4114
+ context.report({
4115
+ node,
4116
+ message: "User-facing variable text must come from src/locales/index.ts, not be written inline."
4117
+ });
4118
+ },
4119
+ CallExpression(node) {
4120
+ if (node.callee.type !== "MemberExpression") return;
4121
+ const objectName = getNodeName(node.callee.object);
4122
+ const methodName = getNodeName(node.callee.property);
4123
+ if (objectName !== "toast" || methodName === null || !TOAST_METHOD_NAMES.has(methodName)) return;
4124
+ if (!containsUserFacingString(node.arguments[0])) return;
4125
+ context.report({
4126
+ node,
4127
+ message: "User-facing toast text must come from src/locales/index.ts, not be written inline."
4128
+ });
4024
4129
  }
4025
4130
  };
4026
4131
  }
@@ -4831,6 +4936,11 @@ function isSubstantial(node) {
4831
4936
  function blockChildren(node) {
4832
4937
  return (node.children ?? []).filter(isJsxNode);
4833
4938
  }
4939
+ function structuralChildren(node) {
4940
+ return (node.children ?? []).filter(
4941
+ (child) => isJsxNode(child) || isJsxExpressionContainer(child)
4942
+ );
4943
+ }
4834
4944
  var repeatedStructureRule = {
4835
4945
  meta: {
4836
4946
  schema: [],
@@ -4850,6 +4960,10 @@ var repeatedStructureRule = {
4850
4960
  }
4851
4961
  };
4852
4962
  function checkChildren(node) {
4963
+ checkSubstantialBlocks(node);
4964
+ checkSmallElementExpressionPairs(node);
4965
+ }
4966
+ function checkSubstantialBlocks(node) {
4853
4967
  const children = blockChildren(node);
4854
4968
  const seen = /* @__PURE__ */ new Map();
4855
4969
  for (const child of children) {
@@ -4862,11 +4976,35 @@ var repeatedStructureRule = {
4862
4976
  if (group.length < 2) continue;
4863
4977
  const first = group[0];
4864
4978
  if (!first || !isSubstantial(first)) continue;
4865
- context.report({
4866
- node: first,
4867
- message: `The same arrangement of elements appears ${String(group.length)} times here; extract it as a named component. Different data or labels do not prevent extraction. See docs/next-codebase-guide/rules/repeated-structure-rule.md`
4868
- });
4979
+ reportRepeatedStructure(first, group.length);
4980
+ }
4981
+ }
4982
+ function checkSmallElementExpressionPairs(node) {
4983
+ const children = structuralChildren(node);
4984
+ const seen = /* @__PURE__ */ new Map();
4985
+ for (let index = 0; index < children.length - 1; index += 1) {
4986
+ const first = children[index];
4987
+ const second = children[index + 1];
4988
+ if (!first || !second || !isJsxNode(first) || !isJsxExpressionContainer(second) || isSubstantial(first)) {
4989
+ continue;
4990
+ }
4991
+ const sig = `${signature(first)}/{}`;
4992
+ const group = seen.get(sig) ?? [];
4993
+ group.push(first);
4994
+ seen.set(sig, group);
4869
4995
  }
4996
+ for (const [, group] of seen) {
4997
+ if (group.length < 2) continue;
4998
+ const first = group[0];
4999
+ if (!first) continue;
5000
+ reportRepeatedStructure(first, group.length);
5001
+ }
5002
+ }
5003
+ function reportRepeatedStructure(node, count) {
5004
+ context.report({
5005
+ node,
5006
+ message: `The same arrangement of elements appears ${String(count)} times here; extract it as a named component. Different data or labels do not prevent extraction. See docs/next-codebase-guide/rules/repeated-structure-rule.md`
5007
+ });
4870
5008
  }
4871
5009
  }
4872
5010
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.29",
3
+ "version": "0.10.31",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",