@estiva-app/ui 0.19.0 → 0.21.0

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.
Files changed (112) hide show
  1. package/README.md +38 -2
  2. package/dist/AttachmentCard.d.ts.map +1 -1
  3. package/dist/Breadcrumb.d.ts.map +1 -1
  4. package/dist/Button.d.ts +6 -1
  5. package/dist/Button.d.ts.map +1 -1
  6. package/dist/Card.d.ts +6 -1
  7. package/dist/Card.d.ts.map +1 -1
  8. package/dist/DialogShell.d.ts.map +1 -1
  9. package/dist/Divider.d.ts +7 -0
  10. package/dist/Divider.d.ts.map +1 -1
  11. package/dist/IconButton.d.ts +12 -2
  12. package/dist/IconButton.d.ts.map +1 -1
  13. package/dist/IdentityMenu.d.ts.map +1 -1
  14. package/dist/Link.d.ts +6 -1
  15. package/dist/Link.d.ts.map +1 -1
  16. package/dist/Menu.d.ts.map +1 -1
  17. package/dist/Popover.d.ts.map +1 -1
  18. package/dist/ReactionPicker.d.ts.map +1 -1
  19. package/dist/SectionLabel.d.ts +6 -1
  20. package/dist/SectionLabel.d.ts.map +1 -1
  21. package/dist/Tooltip.d.ts.map +1 -1
  22. package/dist/eslint/index.d.ts +2 -0
  23. package/dist/eslint/index.d.ts.map +1 -1
  24. package/dist/eslint/index.js +514 -6
  25. package/dist/eslint/index.js.map +4 -4
  26. package/dist/eslint/no-restyled-part.d.ts +81 -0
  27. package/dist/eslint/no-restyled-part.d.ts.map +1 -0
  28. package/dist/gates/app-checks.d.ts +40 -0
  29. package/dist/gates/app-checks.d.ts.map +1 -0
  30. package/dist/gates/chunk-AWO7SZSA.js +477 -0
  31. package/dist/gates/chunk-AWO7SZSA.js.map +7 -0
  32. package/dist/gates/chunk-ZGJ2J5NU.js +754 -0
  33. package/dist/gates/chunk-ZGJ2J5NU.js.map +7 -0
  34. package/dist/gates/cli.d.ts +2 -0
  35. package/dist/gates/cli.d.ts.map +1 -0
  36. package/dist/gates/cli.js +46 -0
  37. package/dist/gates/cli.js.map +7 -0
  38. package/dist/gates/count.d.ts +20 -0
  39. package/dist/gates/count.d.ts.map +1 -0
  40. package/dist/gates/create-app-cli.d.ts +2 -0
  41. package/dist/gates/create-app-cli.d.ts.map +1 -0
  42. package/dist/gates/create-app.d.ts +23 -0
  43. package/dist/gates/create-app.d.ts.map +1 -0
  44. package/dist/gates/create-app.js +30 -0
  45. package/dist/gates/create-app.js.map +7 -0
  46. package/dist/gates/gate-config.d.ts +44 -0
  47. package/dist/gates/gate-config.d.ts.map +1 -0
  48. package/dist/gates/hook.d.ts +18 -0
  49. package/dist/gates/hook.d.ts.map +1 -0
  50. package/dist/gates/index.d.ts +30 -0
  51. package/dist/gates/index.d.ts.map +1 -0
  52. package/dist/gates/index.js +415 -0
  53. package/dist/gates/index.js.map +7 -0
  54. package/dist/gates/status.d.ts +84 -0
  55. package/dist/gates/status.d.ts.map +1 -0
  56. package/dist/gates/token-lint.d.ts +26 -0
  57. package/dist/gates/token-lint.d.ts.map +1 -0
  58. package/dist/index.js +290 -266
  59. package/dist/index.js.map +4 -4
  60. package/package.json +30 -4
  61. package/src/AttachmentCard.tsx +7 -2
  62. package/src/Banner.tsx +2 -2
  63. package/src/Breadcrumb.tsx +9 -4
  64. package/src/Button.mdx +7 -0
  65. package/src/Button.stories.tsx +4 -1
  66. package/src/Button.tsx +10 -2
  67. package/src/Card.mdx +7 -0
  68. package/src/Card.stories.tsx +11 -0
  69. package/src/Card.tsx +8 -0
  70. package/src/CommandPalette.tsx +1 -1
  71. package/src/ConfirmDialog.tsx +2 -2
  72. package/src/DialogShell.tsx +6 -7
  73. package/src/Divider.mdx +3 -0
  74. package/src/Divider.tsx +12 -2
  75. package/src/EditableText.mdx +5 -0
  76. package/src/IconButton.mdx +9 -0
  77. package/src/IconButton.stories.tsx +10 -1
  78. package/src/IconButton.tsx +22 -3
  79. package/src/IdentityMenu.tsx +4 -1
  80. package/src/Link.mdx +6 -0
  81. package/src/Link.stories.tsx +12 -0
  82. package/src/Link.tsx +7 -2
  83. package/src/Menu.stories.tsx +1 -1
  84. package/src/Menu.test.tsx +2 -2
  85. package/src/Menu.tsx +6 -3
  86. package/src/Person.stories.tsx +5 -5
  87. package/src/Popover.test.tsx +2 -2
  88. package/src/Popover.tsx +3 -2
  89. package/src/PreviewCard.tsx +1 -1
  90. package/src/Property.stories.tsx +2 -2
  91. package/src/ReactionPicker.tsx +6 -3
  92. package/src/ScrollArea.stories.tsx +21 -7
  93. package/src/SectionLabel.mdx +3 -2
  94. package/src/SectionLabel.stories.tsx +3 -0
  95. package/src/SectionLabel.tsx +7 -2
  96. package/src/Tooltip.tsx +12 -14
  97. package/src/eslint/index.test.ts +32 -7
  98. package/src/eslint/index.ts +10 -1
  99. package/src/eslint/no-restyled-part.test.ts +301 -0
  100. package/src/eslint/no-restyled-part.ts +647 -0
  101. package/src/gates/app-checks.ts +224 -0
  102. package/src/gates/cli.ts +55 -0
  103. package/src/gates/count.ts +76 -0
  104. package/src/gates/create-app-cli.ts +25 -0
  105. package/src/gates/create-app.test.ts +72 -0
  106. package/src/gates/create-app.ts +797 -0
  107. package/src/gates/gate-config.ts +78 -0
  108. package/src/gates/gates.test.ts +183 -0
  109. package/src/gates/hook.ts +111 -0
  110. package/src/gates/index.ts +30 -0
  111. package/src/gates/status.ts +532 -0
  112. package/src/gates/token-lint.ts +231 -0
@@ -441,10 +441,10 @@ function anchorOf(node) {
441
441
  if (node.type === "FunctionDeclaration") {
442
442
  return node.parent && /^Export/.test(node.parent.type) ? node.parent : node;
443
443
  }
444
- let child = node;
445
- for (let p = node.parent; p; child = p, p = p.parent) {
444
+ let child2 = node;
445
+ for (let p = node.parent; p; child2 = p, p = p.parent) {
446
446
  if (p.type === "JSXOpeningElement" || p.type === "Property" || p.type === "MethodDefinition" || p.type === "PropertyDefinition") return p;
447
- if (p.type === "ArrayExpression") return child;
447
+ if (p.type === "ArrayExpression") return child2;
448
448
  if (p.type !== "BlockStatement" && /(Statement|Declaration)$/.test(p.type)) return p;
449
449
  }
450
450
  return node;
@@ -714,6 +714,509 @@ var noRebuiltBehaviour = {
714
714
  }
715
715
  };
716
716
 
717
+ // src/eslint/no-restyled-part.ts
718
+ import { existsSync as existsSync2, readFileSync, statSync } from "node:fs";
719
+ import { dirname, join, resolve } from "node:path";
720
+ var PLACEMENT = [
721
+ { what: "space around", pattern: /^m[xytrblse]?-/ },
722
+ { what: "space inside", pattern: /^p[xytrblse]?-/ },
723
+ { what: "width and height", pattern: /^(?:w|h|size|min-w|min-h|max-w|max-h)-/ },
724
+ { what: "shown, and how it lays out", pattern: /^(?:hidden|block|inline|inline-block|inline-flex|inline-grid|flex|grid|contents|flow-root)$/ },
725
+ { what: "its place in a row or a grid", pattern: /^(?:grow|shrink)$|^(?:flex|basis|grow|shrink|order|gap|gap-x|gap-y|space-x|space-y|col|row|grid-cols|grid-rows|grid-flow|auto-cols|auto-rows)-/ },
726
+ { what: "alignment", pattern: /^(?:self|justify|justify-items|justify-self|items|place-content|place-items|place-self)-|^content-(?!\[)/ },
727
+ { what: "position", pattern: /^(?:static|relative|absolute|fixed|sticky)$|^(?:inset|inset-x|inset-y|top|right|bottom|left|start|end|z)-/ },
728
+ // Draws nothing: it names the part, so what is inside can answer its hover (`group-hover:`).
729
+ { what: "a name for hover", pattern: /^(?:group|peer)(?:\/[\w-]+)?$/ }
730
+ ];
731
+ var REACHING_VARIANT = /^\[|^(?:before|after|placeholder|file|marker|selection|first-letter|first-line|backdrop|\*|\*\*)$/;
732
+ function splitClass(token) {
733
+ const variants = [];
734
+ let current = "";
735
+ let depth = 0;
736
+ for (const character of token) {
737
+ if (character === "[") depth += 1;
738
+ if (character === "]") depth -= 1;
739
+ if (character === ":" && depth === 0) {
740
+ variants.push(current);
741
+ current = "";
742
+ } else current += character;
743
+ }
744
+ return { variants, utility: current.replace(/^!/, "").replace(/!$/, "").replace(/^-/, "") };
745
+ }
746
+ function isPlacement(token) {
747
+ const { variants, utility } = splitClass(token);
748
+ if (variants.some((variant) => REACHING_VARIANT.test(variant))) return false;
749
+ return PLACEMENT.some(({ pattern }) => pattern.test(utility));
750
+ }
751
+ var isPadding = (token) => {
752
+ const { variants, utility } = splitClass(token);
753
+ return !variants.some((variant) => REACHING_VARIANT.test(variant)) && /^p[xytrblse]?-/.test(utility);
754
+ };
755
+ var PART_LOOK_PROPS = {
756
+ AppShell: ["variant"],
757
+ Avatar: ["size"],
758
+ AvatarGroup: ["size"],
759
+ Banner: ["tone"],
760
+ Button: ["variant", "size"],
761
+ Card: ["fill", "hover", "attention", "selected", "active", "hovered", "quietUntilHover", "unreadable", "clip"],
762
+ Checkbox: ["row"],
763
+ Chip: ["type"],
764
+ Divider: ["tone", "orientation"],
765
+ EmptyState: ["scope"],
766
+ FieldLine: ["tone"],
767
+ IconButton: ["variant", "pressed", "glow"],
768
+ InputChip: ["truncate"],
769
+ Link: ["variant", "truncate"],
770
+ MenuItem: ["size", "selected", "destructive"],
771
+ MenuSub: ["selected"],
772
+ NavItem: ["active"],
773
+ Person: ["size"],
774
+ ProgressBar: ["variant"],
775
+ Property: ["layout"],
776
+ RailItem: ["active"],
777
+ Reaction: ["pressed"],
778
+ ReactionPicker: ["surface"],
779
+ SectionLabel: ["tone"],
780
+ Select: ["size"],
781
+ Tabs: ["size"],
782
+ TextInput: ["size"],
783
+ Toast: ["type"],
784
+ Toolbar: ["surface"],
785
+ ToolbarButton: ["variant", "pressed", "glow"],
786
+ TopBar: ["variant"]
787
+ };
788
+ var CLASS_PROP = /^(?:className|[a-z][A-Za-z]*ClassName)$/;
789
+ var child = (node, key) => node[key];
790
+ var children = (node, key) => node[key] ?? [];
791
+ var nameOf = (node) => node?.type === "Identifier" || node?.type === "JSXIdentifier" ? node.name : node?.type === "Literal" && typeof node.value === "string" ? node.value : void 0;
792
+ function walk(root, visit) {
793
+ const stack = [root];
794
+ while (stack.length) {
795
+ const node = stack.pop();
796
+ visit(node);
797
+ for (const key of Object.keys(node)) {
798
+ if (key === "parent" || key === "loc" || key === "range" || key === "tokens" || key === "comments") continue;
799
+ const value = node[key];
800
+ if (Array.isArray(value)) for (const item of value) {
801
+ if (item && typeof item.type === "string") stack.push(item);
802
+ }
803
+ else if (value && typeof value.type === "string") stack.push(value);
804
+ }
805
+ }
806
+ }
807
+ var PACKAGE = "@estiva-app/ui";
808
+ var moduleCache = /* @__PURE__ */ new Map();
809
+ var packageDirCache = /* @__PURE__ */ new Map();
810
+ var packagePartsCache = /* @__PURE__ */ new Map();
811
+ function parseFile(parser, filePath) {
812
+ const code2 = readFileSync(filePath, "utf8");
813
+ const options = { filePath, ecmaVersion: "latest", sourceType: "module", ecmaFeatures: { jsx: filePath.endsWith("x") }, range: true, loc: true };
814
+ try {
815
+ if (parser.parseForESLint) return parser.parseForESLint(code2, options).ast;
816
+ if (parser.parse) return parser.parse(code2, options);
817
+ } catch {
818
+ }
819
+ return null;
820
+ }
821
+ function packageDirOf(file) {
822
+ const start = dirname(resolve(file));
823
+ if (packageDirCache.has(start)) return packageDirCache.get(start) ?? null;
824
+ let dir = start;
825
+ for (; ; ) {
826
+ if (existsSync2(join(dir, "package.json"))) break;
827
+ const up = dirname(dir);
828
+ if (up === dir) {
829
+ dir = "";
830
+ break;
831
+ }
832
+ dir = up;
833
+ }
834
+ packageDirCache.set(start, dir || null);
835
+ return dir || null;
836
+ }
837
+ function resolveModule(fromFile, specifier) {
838
+ let base;
839
+ if (specifier.startsWith(".")) base = resolve(dirname(fromFile), specifier);
840
+ else if (specifier.startsWith("@/")) {
841
+ const dir = packageDirOf(fromFile);
842
+ if (!dir) return null;
843
+ base = join(dir, "src", specifier.slice(2));
844
+ } else return null;
845
+ for (const candidate of [base, `${base}.tsx`, `${base}.ts`, join(base, "index.tsx"), join(base, "index.ts")]) {
846
+ if (existsSync2(candidate) && statSync(candidate).isFile()) return candidate;
847
+ }
848
+ return null;
849
+ }
850
+ function packagePartsFor(file, parser) {
851
+ const dir = packageDirOf(file);
852
+ if (!dir) return null;
853
+ let name;
854
+ try {
855
+ name = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).name;
856
+ } catch {
857
+ return null;
858
+ }
859
+ if (name !== PACKAGE) return null;
860
+ const index = join(dir, "src", "index.ts");
861
+ if (!existsSync2(index)) return null;
862
+ const mtime = statSync(index).mtimeMs;
863
+ const cached = packagePartsCache.get(index);
864
+ if (cached && cached.mtime === mtime) return cached.parts;
865
+ const parts = /* @__PURE__ */ new Set();
866
+ const ast = parseFile(parser, index);
867
+ for (const statement2 of ast ? children(ast, "body") : []) {
868
+ if (statement2?.type !== "ExportNamedDeclaration" || statement2.exportKind === "type") continue;
869
+ for (const specifier of children(statement2, "specifiers")) {
870
+ const exported = nameOf(child(specifier, "exported"));
871
+ if (specifier?.exportKind !== "type" && exported && /^[A-Z][a-z]/.test(exported)) parts.add(exported);
872
+ }
873
+ }
874
+ packagePartsCache.set(index, { mtime, parts });
875
+ return parts;
876
+ }
877
+ function functionComponents(program) {
878
+ const found = /* @__PURE__ */ new Map();
879
+ const unwrap = (node) => {
880
+ let current = node;
881
+ while (current?.type === "CallExpression" && children(current, "arguments").length) {
882
+ const callee = child(current, "callee");
883
+ const text = callee?.type === "Identifier" ? nameOf(callee) : callee?.type === "MemberExpression" ? nameOf(child(callee, "property")) : void 0;
884
+ if (text !== "forwardRef" && text !== "memo") return null;
885
+ current = children(current, "arguments")[0];
886
+ }
887
+ return current?.type === "ArrowFunctionExpression" || current?.type === "FunctionExpression" ? current : null;
888
+ };
889
+ walk(program, (node) => {
890
+ if (node.type === "FunctionDeclaration") {
891
+ const name = nameOf(child(node, "id"));
892
+ if (name && /^[A-Z]/.test(name)) found.set(name, node);
893
+ } else if (node.type === "VariableDeclarator") {
894
+ const name = nameOf(child(node, "id"));
895
+ const fn = unwrap(child(node, "init"));
896
+ if (name && /^[A-Z]/.test(name) && fn) found.set(name, fn);
897
+ }
898
+ });
899
+ return found;
900
+ }
901
+ function moduleParts(file, program, parser, stack) {
902
+ const locals = /* @__PURE__ */ new Map();
903
+ const packageParts = packagePartsFor(file, parser);
904
+ const body = children(program, "body");
905
+ const exportsOf = (specifier) => {
906
+ if (specifier === PACKAGE) return "package";
907
+ const target = resolveModule(file, specifier);
908
+ if (!target) return null;
909
+ if (packageParts) return "package";
910
+ return moduleExports(target, parser, stack);
911
+ };
912
+ for (const statement2 of body) {
913
+ if (statement2?.type !== "ImportDeclaration" || statement2.importKind === "type") continue;
914
+ const source = exportsOf(String(child(statement2, "source").value));
915
+ if (!source) continue;
916
+ for (const specifier of children(statement2, "specifiers")) {
917
+ if (!specifier || specifier.importKind === "type") continue;
918
+ const local = nameOf(child(specifier, "local"));
919
+ if (!local) continue;
920
+ if (specifier.type === "ImportNamespaceSpecifier") {
921
+ if (source === "package" && !packageParts) locals.set(local, "namespace");
922
+ continue;
923
+ }
924
+ const imported = specifier.type === "ImportDefaultSpecifier" ? "default" : nameOf(child(specifier, "imported"));
925
+ if (!imported) continue;
926
+ if (source === "package") {
927
+ const isPart = packageParts ? packageParts.has(imported) : /^[A-Z]/.test(imported);
928
+ if (isPart) locals.set(local, { part: imported, props: "all" });
929
+ } else if (source.has(imported)) locals.set(local, source.get(imported));
930
+ }
931
+ }
932
+ const components = functionComponents(program);
933
+ if (packageParts) {
934
+ for (const name of components.keys()) if (packageParts.has(name) && !locals.has(name)) locals.set(name, { part: name, props: "all" });
935
+ }
936
+ if (!packageParts) {
937
+ let changed = true;
938
+ for (let round = 0; changed && round < 10; round += 1) {
939
+ changed = false;
940
+ for (const [name, fn] of components) {
941
+ const binding = handsOnTo(fn, (tag) => bindingOfTag(tag, locals));
942
+ if (!binding) continue;
943
+ const next = { ...binding, via: name };
944
+ const previous = locals.get(name);
945
+ if (!previous || JSON.stringify(previous) !== JSON.stringify(next)) {
946
+ if (previous && previous !== "namespace" && !previous.via) continue;
947
+ locals.set(name, next);
948
+ changed = true;
949
+ }
950
+ }
951
+ }
952
+ }
953
+ const exports = /* @__PURE__ */ new Map();
954
+ const localBinding = (name) => {
955
+ const binding = name ? locals.get(name) : void 0;
956
+ return binding && binding !== "namespace" ? binding : void 0;
957
+ };
958
+ for (const statement2 of body) {
959
+ if (!statement2) continue;
960
+ if (statement2.type === "ExportNamedDeclaration") {
961
+ const declaration = child(statement2, "declaration");
962
+ if (declaration?.type === "FunctionDeclaration") {
963
+ const name = nameOf(child(declaration, "id"));
964
+ const binding = localBinding(name);
965
+ if (name && binding) exports.set(name, binding);
966
+ } else if (declaration?.type === "VariableDeclaration") {
967
+ for (const declarator of children(declaration, "declarations")) {
968
+ const name = nameOf(child(declarator, "id"));
969
+ const init = child(declarator, "init");
970
+ const binding = localBinding(name) ?? (init?.type === "Identifier" ? localBinding(nameOf(init)) : void 0);
971
+ if (name && binding) exports.set(name, binding);
972
+ }
973
+ }
974
+ const source = child(statement2, "source");
975
+ const from = source ? exportsOf(String(source.value)) : null;
976
+ for (const specifier of children(statement2, "specifiers")) {
977
+ if (!specifier || specifier.exportKind === "type") continue;
978
+ const local = nameOf(child(specifier, "local"));
979
+ const exported = nameOf(child(specifier, "exported"));
980
+ if (!local || !exported) continue;
981
+ if (!source) {
982
+ const binding = localBinding(local);
983
+ if (binding) exports.set(exported, binding);
984
+ } else if (from === "package") {
985
+ if (/^[A-Z]/.test(local)) exports.set(exported, { part: local, props: "all" });
986
+ } else if (from?.has(local)) exports.set(exported, from.get(local));
987
+ }
988
+ } else if (statement2.type === "ExportAllDeclaration" && !child(statement2, "exported")) {
989
+ const from = exportsOf(String(child(statement2, "source").value));
990
+ if (from && from !== "package") for (const [name, binding] of from) exports.set(name, binding);
991
+ } else if (statement2.type === "ExportDefaultDeclaration") {
992
+ const declaration = child(statement2, "declaration");
993
+ const name = declaration?.type === "Identifier" ? nameOf(declaration) : nameOf(child(declaration, "id"));
994
+ const binding = localBinding(name);
995
+ if (binding) exports.set("default", binding);
996
+ }
997
+ }
998
+ return { locals, exports };
999
+ }
1000
+ function moduleExports(file, parser, stack) {
1001
+ const mtime = statSync(file).mtimeMs;
1002
+ const cached = moduleCache.get(file);
1003
+ if (cached && cached.mtime === mtime) return cached.exports;
1004
+ if (stack.has(file)) return /* @__PURE__ */ new Map();
1005
+ stack.add(file);
1006
+ const program = parseFile(parser, file);
1007
+ const exports = program ? moduleParts(file, program, parser, stack).exports : /* @__PURE__ */ new Map();
1008
+ stack.delete(file);
1009
+ moduleCache.set(file, { mtime, exports });
1010
+ return exports;
1011
+ }
1012
+ function bindingOfTag(tag, locals) {
1013
+ if (!tag) return void 0;
1014
+ if (tag.type === "JSXIdentifier") {
1015
+ const binding = locals.get(tag.name);
1016
+ return binding && binding !== "namespace" ? binding : void 0;
1017
+ }
1018
+ if (tag.type === "JSXMemberExpression") {
1019
+ const object = child(tag, "object");
1020
+ const property = nameOf(child(tag, "property"));
1021
+ if (object?.type === "JSXIdentifier" && locals.get(object.name) === "namespace" && property) return { part: property, props: "all" };
1022
+ }
1023
+ return void 0;
1024
+ }
1025
+ function handsOnTo(fn, bindingOf) {
1026
+ const param = children(fn, "params")[0];
1027
+ if (!param) return void 0;
1028
+ let restName;
1029
+ let propsName;
1030
+ const classParams = /* @__PURE__ */ new Map();
1031
+ if (param.type === "ObjectPattern") {
1032
+ for (const property of children(param, "properties")) {
1033
+ if (!property) continue;
1034
+ if (property.type === "RestElement") restName = nameOf(child(property, "argument"));
1035
+ else if (property.type === "Property") {
1036
+ const key = nameOf(child(property, "key"));
1037
+ let value = child(property, "value");
1038
+ if (value?.type === "AssignmentPattern") value = child(value, "left");
1039
+ const local = nameOf(value);
1040
+ if (key && local && CLASS_PROP.test(key)) classParams.set(local, key);
1041
+ }
1042
+ }
1043
+ } else if (param.type === "Identifier") propsName = nameOf(param);
1044
+ let wrapper;
1045
+ const forwarded = { props: {} };
1046
+ const body = child(fn, "body");
1047
+ if (!body) return void 0;
1048
+ walk(body, (node) => {
1049
+ if (wrapper || node.type !== "JSXOpeningElement") return;
1050
+ const binding = bindingOf(child(node, "name"));
1051
+ if (!binding) return;
1052
+ for (const attribute2 of children(node, "attributes")) {
1053
+ if (!attribute2) continue;
1054
+ if (attribute2.type === "JSXSpreadAttribute") {
1055
+ const argument = nameOf(child(attribute2, "argument"));
1056
+ if (argument && (argument === restName || argument === propsName)) wrapper = { part: binding.part, props: binding.props };
1057
+ continue;
1058
+ }
1059
+ const prop = nameOf(child(attribute2, "name"));
1060
+ if (!prop || !CLASS_PROP.test(prop)) continue;
1061
+ const partProp = binding.props === "all" ? prop : binding.props[prop];
1062
+ if (!partProp) continue;
1063
+ walk(attribute2, (inner) => {
1064
+ let callerProp;
1065
+ if (inner.type === "Identifier") callerProp = classParams.get(inner.name);
1066
+ if (inner.type === "MemberExpression" && nameOf(child(inner, "object")) === propsName) {
1067
+ const key = nameOf(child(inner, "property"));
1068
+ if (key && CLASS_PROP.test(key)) callerProp = key;
1069
+ }
1070
+ if (!callerProp) return;
1071
+ if (forwarded.part && forwarded.part !== binding.part) return;
1072
+ forwarded.part = binding.part;
1073
+ forwarded.props[callerProp] = partProp;
1074
+ });
1075
+ }
1076
+ });
1077
+ if (wrapper) return wrapper;
1078
+ return forwarded.part ? { part: forwarded.part, props: forwarded.props } : void 0;
1079
+ }
1080
+ var CLASS_FUNCTIONS = /* @__PURE__ */ new Set(["cn", "clsx", "cx", "twMerge", "classNames"]);
1081
+ function constantOf(identifier, scope) {
1082
+ let current = scope;
1083
+ while (current) {
1084
+ const variable = current.set.get(identifier.name);
1085
+ if (variable) {
1086
+ const definition = variable.defs[0];
1087
+ if (variable.defs.length !== 1 || definition?.type !== "Variable") return void 0;
1088
+ if (definition.parent?.kind !== "const") return void 0;
1089
+ return child(definition.node, "init") ?? void 0;
1090
+ }
1091
+ current = current.upper;
1092
+ }
1093
+ return void 0;
1094
+ }
1095
+ function classesOf(value, scopeOf, asMap = false, seen = /* @__PURE__ */ new Set()) {
1096
+ if (!value || seen.has(value) || seen.size > 200) return [];
1097
+ seen.add(value);
1098
+ const read = (node, map = false) => classesOf(node, scopeOf, map, seen);
1099
+ const words = (text) => typeof text === "string" ? text.split(/\s+/).filter(Boolean) : [];
1100
+ switch (value.type) {
1101
+ case "Literal":
1102
+ return words(value.value);
1103
+ case "TemplateLiteral":
1104
+ return [...children(value, "quasis").flatMap((quasi) => words((quasi?.value).cooked)), ...children(value, "expressions").flatMap((e) => read(e))];
1105
+ case "JSXExpressionContainer":
1106
+ case "TSAsExpression":
1107
+ case "TSSatisfiesExpression":
1108
+ case "TSNonNullExpression":
1109
+ case "ChainExpression":
1110
+ return read(child(value, "expression"), asMap);
1111
+ case "ConditionalExpression":
1112
+ return [...read(child(value, "consequent")), ...read(child(value, "alternate"))];
1113
+ case "LogicalExpression":
1114
+ return value.operator === "&&" ? read(child(value, "right")) : [...read(child(value, "left")), ...read(child(value, "right"))];
1115
+ case "BinaryExpression":
1116
+ return value.operator === "+" ? [...read(child(value, "left")), ...read(child(value, "right"))] : [];
1117
+ case "ArrayExpression":
1118
+ return children(value, "elements").flatMap((e) => read(e));
1119
+ case "ObjectExpression":
1120
+ return children(value, "properties").flatMap((property) => {
1121
+ if (property?.type !== "Property") return [];
1122
+ return asMap ? read(child(property, "value"), true) : property.computed ? [] : words(nameOf(child(property, "key")));
1123
+ });
1124
+ case "CallExpression": {
1125
+ const callee = child(value, "callee");
1126
+ return callee?.type === "Identifier" && CLASS_FUNCTIONS.has(callee.name) ? children(value, "arguments").flatMap((a) => read(a)) : [];
1127
+ }
1128
+ case "Identifier": {
1129
+ const constant = constantOf(value, scopeOf(value));
1130
+ return constant ? read(constant, asMap) : [];
1131
+ }
1132
+ case "MemberExpression": {
1133
+ const object = child(value, "object");
1134
+ const target = object?.type === "Identifier" ? constantOf(object, scopeOf(object)) : object;
1135
+ const map = target?.type === "TSAsExpression" || target?.type === "TSSatisfiesExpression" ? child(target, "expression") : target;
1136
+ if (map?.type !== "ObjectExpression") return [];
1137
+ const property = child(value, "property");
1138
+ const key = value.computed ? property?.type === "Literal" ? String(property.value) : void 0 : nameOf(property);
1139
+ return children(map, "properties").flatMap((entry) => {
1140
+ if (entry?.type !== "Property") return [];
1141
+ if (key !== void 0 && nameOf(child(entry, "key")) !== key) return [];
1142
+ return read(child(entry, "value"), true);
1143
+ });
1144
+ }
1145
+ default:
1146
+ return [];
1147
+ }
1148
+ }
1149
+ var code = (items) => items.map((item) => `\`${item}\``).join(" ");
1150
+ var or = (items) => items.length === 1 ? code(items) : `${items.slice(0, -1).map((i) => `\`${i}\``).join(", ")} or \`${items[items.length - 1]}\``;
1151
+ var noRestyledPart = {
1152
+ meta: {
1153
+ type: "problem",
1154
+ docs: { description: "Only placement passes into a part of @estiva-app/ui through a class prop" },
1155
+ schema: [],
1156
+ messages: {
1157
+ restyled: "{{classes}} on {{where}} changes how it looks. A part of @estiva-app/ui is placed from outside, never restyled: only space, size, flex and grid, and position pass in.{{use}} A look for what is around it goes on your own element around it.",
1158
+ emptyStatePadding: "{{classes}} pads {{where}}. EmptyState takes no padding (Katerina, 14 September): its room comes from the box its rows live in, so put the padding on that box.",
1159
+ ...ESCAPE_MESSAGES
1160
+ }
1161
+ },
1162
+ create(context) {
1163
+ const parser = context.languageOptions?.parser ?? {};
1164
+ const file = context.filename;
1165
+ const sourceCode = context.sourceCode;
1166
+ const scopeOf = (node) => sourceCode.getScope(node);
1167
+ let locals;
1168
+ const escapes = /* @__PURE__ */ new Map();
1169
+ const escaped = (anchor) => {
1170
+ if (!escapes.has(anchor)) escapes.set(anchor, isEscaped(context, anchor));
1171
+ return escapes.get(anchor) === true;
1172
+ };
1173
+ const check = (element, binding, prop, value, at) => {
1174
+ const partProp = binding.props === "all" ? prop : binding.props[prop];
1175
+ if (!partProp || !value) return;
1176
+ const classes = classesOf(value, scopeOf);
1177
+ const handedOn = binding.props === "all" ? "its props" : `its \`${prop}\``;
1178
+ const via = binding.via && binding.via !== binding.part ? `\`${binding.via}\` (it hands ${handedOn} to \`${binding.part}\`)` : `\`${binding.part}\``;
1179
+ const where = `${via}${partProp === "className" ? "" : `'s \`${partProp}\``}`;
1180
+ const restyled = [...new Set(classes.filter((token) => !isPlacement(token)))];
1181
+ const padding = binding.part === "EmptyState" && binding.props === "all" ? [...new Set(classes.filter(isPadding))] : [];
1182
+ if (!restyled.length && !padding.length) return;
1183
+ if (escaped(element)) return;
1184
+ if (restyled.length) {
1185
+ const props = PART_LOOK_PROPS[binding.part];
1186
+ const use = props ? ` Use its ${or(props)}.` : ` \`${binding.part}\` has no prop for this yet: ask Katerina, and it gets made in @estiva-app/ui.`;
1187
+ context.report({ loc: at.loc, messageId: "restyled", data: { classes: code(restyled), where, use } });
1188
+ }
1189
+ if (padding.length) context.report({ loc: at.loc, messageId: "emptyStatePadding", data: { classes: code(padding), where } });
1190
+ };
1191
+ return {
1192
+ Program(program) {
1193
+ locals = moduleParts(file, program, parser, /* @__PURE__ */ new Set([file])).locals;
1194
+ },
1195
+ JSXOpeningElement(ruleNode) {
1196
+ const element = ruleNode;
1197
+ const binding = locals && bindingOfTag(child(element, "name"), locals);
1198
+ if (!binding) return;
1199
+ for (const attribute2 of children(element, "attributes")) {
1200
+ if (!attribute2) continue;
1201
+ if (attribute2.type === "JSXAttribute") {
1202
+ const prop = nameOf(child(attribute2, "name"));
1203
+ if (prop && CLASS_PROP.test(prop)) check(element, binding, prop, child(attribute2, "value"), attribute2);
1204
+ continue;
1205
+ }
1206
+ let spread = child(attribute2, "argument");
1207
+ if (spread?.type === "Identifier") spread = constantOf(spread, scopeOf(spread));
1208
+ while (spread?.type === "TSAsExpression" || spread?.type === "TSSatisfiesExpression") spread = child(spread, "expression");
1209
+ if (spread?.type !== "ObjectExpression") continue;
1210
+ for (const property of children(spread, "properties")) {
1211
+ const key = property?.type === "Property" && !property.computed ? nameOf(child(property, "key")) : void 0;
1212
+ if (key && CLASS_PROP.test(key)) check(element, binding, key, child(property, "value"), property);
1213
+ }
1214
+ }
1215
+ }
1216
+ };
1217
+ }
1218
+ };
1219
+
717
1220
  // src/eslint/raw-element-outside-a-wrapper.ts
718
1221
  var RAW_ELEMENTS = /* @__PURE__ */ new Set(["a", "button", "input", "textarea", "select", "dialog", "form", "label"]);
719
1222
  var rawElementOutsideAWrapper = {
@@ -755,13 +1258,15 @@ var { version } = createRequire(import.meta.url)("../../package.json");
755
1258
  var PLUGIN_KEY = "estiva";
756
1259
  var appRules = {
757
1260
  "no-raw-element": noRawElement,
758
- "no-rebuilt-behaviour": noRebuiltBehaviour
1261
+ "no-rebuilt-behaviour": noRebuiltBehaviour,
1262
+ "no-restyled-part": noRestyledPart
759
1263
  };
760
1264
  var packageRules = {
761
1265
  "raw-element-outside-a-wrapper": rawElementOutsideAWrapper,
762
1266
  "no-hand-rolled-behaviour": noHandRolledBehaviour,
763
1267
  "component-has-a-page": componentHasAPage,
764
- "component-has-a-story": componentHasAStory
1268
+ "component-has-a-story": componentHasAStory,
1269
+ "no-restyled-part": noRestyledPart
765
1270
  };
766
1271
  var rules = { ...appRules, ...packageRules };
767
1272
  var plugin = {
@@ -776,7 +1281,8 @@ plugin.configs.recommended = {
776
1281
  plugins: { [PLUGIN_KEY]: plugin },
777
1282
  rules: {
778
1283
  [`${PLUGIN_KEY}/no-raw-element`]: "error",
779
- [`${PLUGIN_KEY}/no-rebuilt-behaviour`]: "error"
1284
+ [`${PLUGIN_KEY}/no-rebuilt-behaviour`]: "error",
1285
+ [`${PLUGIN_KEY}/no-restyled-part`]: "error"
780
1286
  }
781
1287
  };
782
1288
  plugin.configs.strict = {
@@ -814,6 +1320,8 @@ export {
814
1320
  MIN_REASON,
815
1321
  OWNED_BEHAVIOURS,
816
1322
  PACKAGE_RULE_IDS,
1323
+ PART_LOOK_PROPS,
1324
+ PLACEMENT,
817
1325
  PLUGIN_KEY,
818
1326
  SETTINGS_KEY,
819
1327
  countGates,