@gunkin/uiify 0.0.16-alpha → 0.0.19-alpha

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 (98) hide show
  1. package/README.md +34 -9
  2. package/package.json +1 -1
  3. package/skills/uiify/SKILL.md +125 -125
  4. package/skills/uiify/references/compositions/card-rhythm.md +14 -0
  5. package/skills/uiify/references/compositions/destructive-confirmation.md +15 -0
  6. package/skills/uiify/references/compositions/form-grouping.md +14 -0
  7. package/skills/uiify/references/compositions/hero-actions.md +14 -0
  8. package/skills/uiify/references/compositions/page-frame.md +14 -0
  9. package/skills/uiify/references/compositions/pricing-actions.md +13 -0
  10. package/skills/uiify/references/compositions/save-feedback.md +13 -0
  11. package/skills/uiify/references/compositions/table-controls.md +14 -0
  12. package/skills/uiify/references/elements/accordion.md +38 -0
  13. package/skills/uiify/references/elements/alert-modal.md +42 -0
  14. package/skills/uiify/references/elements/avatar.md +47 -0
  15. package/skills/uiify/references/elements/badge.md +47 -0
  16. package/skills/uiify/references/elements/bar-chart.md +61 -0
  17. package/skills/uiify/references/elements/breadcrumb.md +38 -0
  18. package/skills/uiify/references/elements/button.md +49 -0
  19. package/skills/uiify/references/elements/candle-chart.md +38 -0
  20. package/skills/uiify/references/elements/card.md +51 -0
  21. package/skills/uiify/references/elements/carousel.md +38 -0
  22. package/skills/uiify/references/elements/checkbox.md +39 -0
  23. package/skills/uiify/references/elements/code-block.md +50 -0
  24. package/skills/uiify/references/elements/collapsible.md +38 -0
  25. package/skills/uiify/references/elements/color-picker.md +39 -0
  26. package/skills/uiify/references/elements/combobox.md +37 -0
  27. package/skills/uiify/references/elements/command-palette.md +43 -0
  28. package/skills/uiify/references/elements/context-menu.md +38 -0
  29. package/skills/uiify/references/elements/cookie-banner.md +51 -0
  30. package/skills/uiify/references/elements/date-picker.md +43 -0
  31. package/skills/uiify/references/elements/divider.md +46 -0
  32. package/skills/uiify/references/elements/drawer.md +38 -0
  33. package/skills/uiify/references/elements/dropdown-menu.md +38 -0
  34. package/skills/uiify/references/elements/empty-state.md +50 -0
  35. package/skills/uiify/references/elements/feature-grid.md +54 -0
  36. package/skills/uiify/references/elements/field.md +44 -0
  37. package/skills/uiify/references/elements/file-upload.md +40 -0
  38. package/skills/uiify/references/elements/footer.md +45 -0
  39. package/skills/uiify/references/elements/form.md +41 -0
  40. package/skills/uiify/references/elements/header.md +42 -0
  41. package/skills/uiify/references/elements/hero.md +54 -0
  42. package/skills/uiify/references/elements/hover-card.md +38 -0
  43. package/skills/uiify/references/elements/image-gallery.md +52 -0
  44. package/skills/uiify/references/elements/inline-alert.md +46 -0
  45. package/skills/uiify/references/elements/input.md +41 -0
  46. package/skills/uiify/references/elements/line-chart.md +38 -0
  47. package/skills/uiify/references/elements/logo-wall.md +49 -0
  48. package/skills/uiify/references/elements/modal.md +38 -0
  49. package/skills/uiify/references/elements/number-field.md +39 -0
  50. package/skills/uiify/references/elements/otp-input.md +39 -0
  51. package/skills/uiify/references/elements/pagination.md +41 -0
  52. package/skills/uiify/references/elements/password-input.md +43 -0
  53. package/skills/uiify/references/elements/popup.md +38 -0
  54. package/skills/uiify/references/elements/pricing-table.md +66 -0
  55. package/skills/uiify/references/elements/progress-bar.md +47 -0
  56. package/skills/uiify/references/elements/radio-group.md +39 -0
  57. package/skills/uiify/references/elements/rating.md +39 -0
  58. package/skills/uiify/references/elements/search-input.md +45 -0
  59. package/skills/uiify/references/elements/segmented-control.md +39 -0
  60. package/skills/uiify/references/elements/select.md +42 -0
  61. package/skills/uiify/references/elements/sidebar.md +43 -0
  62. package/skills/uiify/references/elements/skeleton.md +38 -0
  63. package/skills/uiify/references/elements/slider.md +39 -0
  64. package/skills/uiify/references/elements/spinner.md +38 -0
  65. package/skills/uiify/references/elements/stats-bar.md +49 -0
  66. package/skills/uiify/references/elements/stepper.md +43 -0
  67. package/skills/uiify/references/elements/switch.md +41 -0
  68. package/skills/uiify/references/elements/table-of-contents.md +39 -0
  69. package/skills/uiify/references/elements/table.md +43 -0
  70. package/skills/uiify/references/elements/tabs.md +38 -0
  71. package/skills/uiify/references/elements/tags-input.md +39 -0
  72. package/skills/uiify/references/elements/testimonial.md +52 -0
  73. package/skills/uiify/references/elements/textarea.md +41 -0
  74. package/skills/uiify/references/elements/timeline.md +48 -0
  75. package/skills/uiify/references/elements/toast.md +39 -0
  76. package/skills/uiify/references/elements/toggle-group.md +39 -0
  77. package/skills/uiify/references/elements/toggle.md +39 -0
  78. package/skills/uiify/references/elements/toolbar.md +39 -0
  79. package/skills/uiify/references/elements/tooltip.md +38 -0
  80. package/skills/uiify/references/elements/virtual-scroll.md +39 -0
  81. package/skills/uiify/references/examples/alert-confirmation.md +34 -0
  82. package/skills/uiify/references/examples/field-validation.md +37 -0
  83. package/skills/uiify/references/examples/select-basic.md +35 -0
  84. package/skills/uiify/references/examples/table-controls.md +40 -0
  85. package/skills/uiify/references/guides/assets.md +14 -0
  86. package/skills/uiify/references/guides/content.md +11 -0
  87. package/skills/uiify/references/guides/hierarchy.md +14 -0
  88. package/skills/uiify/references/guides/reference.md +9 -0
  89. package/skills/uiify/references/guides/states.md +12 -0
  90. package/skills/uiify/references/guides/surface.md +27 -0
  91. package/skills/uiify/references/laws/accessibility.md +11 -0
  92. package/skills/uiify/references/laws/error-prevention.md +12 -0
  93. package/skills/uiify/references/laws/feedback.md +9 -0
  94. package/skills/uiify/references/laws/proximity.md +10 -0
  95. package/skills/uiify/references/laws/readability.md +9 -0
  96. package/skills/uiify/references/laws/von-restorff.md +9 -0
  97. package/skills/uiify/scripts/lint.mjs +1 -1
  98. package/skills/uiify/scripts/static.mjs +71 -14
@@ -44,7 +44,7 @@ async function lint() {
44
44
  }
45
45
  }
46
46
  }
47
- console.log(`${count} findings (${checks.length} accepted static checks).`);
47
+ console.log(`${count} findings (${checks.length} accepted static checks: ${checks.map((check) => check.id).join(", ") || "none"}).`);
48
48
  process.exitCode = count > 0 ? 1 : 0;
49
49
  }
50
50
 
@@ -41,6 +41,62 @@ function positiveLiteral(attribute, typescript) {
41
41
  );
42
42
  }
43
43
 
44
+ const UIIFY_MODULE = /^@gunkin\/uiify\/(?:components|elements)(?:\/[a-z0-9-]+)?$/;
45
+
46
+ function uiifyBindings(tree, typescript) {
47
+ const bindings = new Set();
48
+ for (const statement of tree.statements) {
49
+ if (
50
+ !typescript.isImportDeclaration(statement) ||
51
+ !typescript.isStringLiteral(statement.moduleSpecifier) ||
52
+ !UIIFY_MODULE.test(statement.moduleSpecifier.text)
53
+ ) continue;
54
+ const clause = statement.importClause;
55
+ if (!clause || clause.isTypeOnly) continue;
56
+ if (clause.name) bindings.add(clause.name.text);
57
+ const namedBindings = clause.namedBindings;
58
+ if (namedBindings && typescript.isNamespaceImport(namedBindings)) {
59
+ bindings.add(namedBindings.name.text);
60
+ } else if (namedBindings && typescript.isNamedImports(namedBindings)) {
61
+ for (const specifier of namedBindings.elements) {
62
+ if (!specifier.isTypeOnly) bindings.add(specifier.name.text);
63
+ }
64
+ }
65
+ }
66
+
67
+ const shadowable = new Set([
68
+ typescript.SyntaxKind.VariableDeclaration,
69
+ typescript.SyntaxKind.BindingElement,
70
+ typescript.SyntaxKind.Parameter,
71
+ typescript.SyntaxKind.FunctionDeclaration,
72
+ typescript.SyntaxKind.FunctionExpression,
73
+ typescript.SyntaxKind.ClassDeclaration,
74
+ typescript.SyntaxKind.ClassExpression,
75
+ ]);
76
+ const visit = (node) => {
77
+ if (shadowable.has(node.kind) && node.name && typescript.isIdentifier(node.name)) {
78
+ bindings.delete(node.name.text);
79
+ }
80
+ typescript.forEachChild(node, visit);
81
+ };
82
+ visit(tree);
83
+ return bindings;
84
+ }
85
+
86
+ function jsxTarget(tagName, bindings, typescript) {
87
+ if (typescript.isIdentifier(tagName)) {
88
+ if (/^[a-z][a-z0-9-]*$/.test(tagName.text)) return { kind: "intrinsic", tag: tagName.text };
89
+ if (bindings.has(tagName.text)) return { kind: "uiify", name: tagName.text };
90
+ return null;
91
+ }
92
+ if (!typescript.isPropertyAccessExpression(tagName)) return null;
93
+ let root = tagName.expression;
94
+ while (typescript.isPropertyAccessExpression(root)) root = root.expression;
95
+ return typescript.isIdentifier(root) && bindings.has(root.text)
96
+ ? { kind: "uiify", name: tagName.getText() }
97
+ : null;
98
+ }
99
+
44
100
  function sourceCheck(id, law, message, violates) {
45
101
  return {
46
102
  id,
@@ -49,20 +105,19 @@ function sourceCheck(id, law, message, violates) {
49
105
  run(input) {
50
106
  const tree = parse(input);
51
107
  const { file, typescript } = input;
108
+ const bindings = uiifyBindings(tree, typescript);
52
109
  const findings = [];
53
110
  const visit = (node) => {
54
- if (
55
- (typescript.isJsxOpeningElement(node) || typescript.isJsxSelfClosingElement(node)) &&
56
- typescript.isIdentifier(node.tagName) &&
57
- /^[a-z][a-z0-9-]*$/.test(node.tagName.text) &&
58
- violates(node.tagName.text, attributes(node, typescript), typescript)
59
- ) {
60
- findings.push({
61
- file,
62
- line: tree.getLineAndCharacterOfPosition(node.getStart(tree)).line + 1,
63
- rule: id,
64
- message,
65
- });
111
+ if (typescript.isJsxOpeningElement(node) || typescript.isJsxSelfClosingElement(node)) {
112
+ const target = jsxTarget(node.tagName, bindings, typescript);
113
+ if (target && violates(target, attributes(node, typescript), typescript)) {
114
+ findings.push({
115
+ file,
116
+ line: tree.getLineAndCharacterOfPosition(node.getStart(tree)).line + 1,
117
+ rule: id,
118
+ message,
119
+ });
120
+ }
66
121
  }
67
122
  typescript.forEachChild(node, visit);
68
123
  };
@@ -84,12 +139,14 @@ export const staticChecks = [
84
139
  "button-type",
85
140
  "error-prevention",
86
141
  "Give the intrinsic button an explicit type.",
87
- (tag, attrs) => tag === "button" && !attrs.some((attr) => attr.name.text === "type"),
142
+ (target, attrs) =>
143
+ target.kind === "intrinsic" && target.tag === "button" && !attrs.some((attr) => attr.name.text === "type"),
88
144
  ),
89
145
  sourceCheck(
90
146
  "image-alt",
91
147
  "accessibility",
92
148
  "Give the intrinsic image an alt attribute (empty for decoration).",
93
- (tag, attrs) => tag === "img" && !attrs.some((attr) => attr.name.text === "alt"),
149
+ (target, attrs) =>
150
+ target.kind === "intrinsic" && target.tag === "img" && !attrs.some((attr) => attr.name.text === "alt"),
94
151
  ),
95
152
  ];