@estiva-app/ui 0.32.0 → 0.34.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 (103) hide show
  1. package/README.md +13 -1
  2. package/dist/AttachmentCard.d.ts.map +1 -1
  3. package/dist/Button.d.ts.map +1 -1
  4. package/dist/Card.d.ts.map +1 -1
  5. package/dist/Chip.d.ts.map +1 -1
  6. package/dist/ChipInput.d.ts.map +1 -1
  7. package/dist/CommandPalette.d.ts.map +1 -1
  8. package/dist/DialogShell.d.ts.map +1 -1
  9. package/dist/Lightbox.d.ts.map +1 -1
  10. package/dist/Menu.d.ts.map +1 -1
  11. package/dist/NavItem.d.ts.map +1 -1
  12. package/dist/PersonTrigger.d.ts.map +1 -1
  13. package/dist/Reaction.d.ts.map +1 -1
  14. package/dist/SearchInput.d.ts.map +1 -1
  15. package/dist/SectionLabel.d.ts +9 -2
  16. package/dist/SectionLabel.d.ts.map +1 -1
  17. package/dist/Select.d.ts.map +1 -1
  18. package/dist/TextInput.d.ts.map +1 -1
  19. package/dist/Textarea.d.ts.map +1 -1
  20. package/dist/Tooltip.d.ts.map +1 -1
  21. package/dist/eslint/index.d.ts +4 -0
  22. package/dist/eslint/index.d.ts.map +1 -1
  23. package/dist/eslint/index.js +449 -28
  24. package/dist/eslint/index.js.map +4 -4
  25. package/dist/eslint/looks-of.d.ts +54 -0
  26. package/dist/eslint/looks-of.d.ts.map +1 -0
  27. package/dist/eslint/no-copied-look.d.ts +3 -0
  28. package/dist/eslint/no-copied-look.d.ts.map +1 -0
  29. package/dist/eslint/no-native-title.d.ts +31 -0
  30. package/dist/eslint/no-native-title.d.ts.map +1 -0
  31. package/dist/gates/index.js +1 -1
  32. package/dist/gates/index.js.map +2 -2
  33. package/dist/index.js +171 -133
  34. package/dist/index.js.map +3 -3
  35. package/dist/looks.d.ts +34 -0
  36. package/dist/looks.d.ts.map +1 -0
  37. package/dist/registry/app-3ALZE6XO.js +10 -0
  38. package/dist/registry/app.d.ts.map +1 -1
  39. package/dist/registry/{build-LGFCCOLR.js → build-NDXMVFQ5.js} +3 -3
  40. package/dist/registry/build.d.ts.map +1 -1
  41. package/dist/registry/{chunk-XCBN2EKW.js → chunk-3WJ7ZZ7Z.js} +8 -4
  42. package/dist/registry/chunk-3WJ7ZZ7Z.js.map +7 -0
  43. package/dist/registry/{chunk-E4JNV7PC.js → chunk-PZO4BVV3.js} +2 -1
  44. package/dist/registry/chunk-PZO4BVV3.js.map +7 -0
  45. package/dist/registry/chunk-XVII7P6G.js +595 -0
  46. package/dist/registry/chunk-XVII7P6G.js.map +7 -0
  47. package/dist/registry/cli.js +3 -3
  48. package/dist/registry/index.js +3 -3
  49. package/dist/registry/schema.d.ts +8 -0
  50. package/dist/registry/schema.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/registry.json +347 -90
  53. package/src/AttachmentCard.tsx +2 -0
  54. package/src/Button.tsx +1 -0
  55. package/src/Card.stories.tsx +2 -2
  56. package/src/Card.tsx +1 -0
  57. package/src/Chip.tsx +2 -1
  58. package/src/ChipInput.tsx +4 -2
  59. package/src/CommandPalette.tsx +2 -0
  60. package/src/ContainerHeader.stories.tsx +2 -1
  61. package/src/DialogShell.tsx +1 -0
  62. package/src/Divider.stories.tsx +12 -10
  63. package/src/Lightbox.tsx +1 -0
  64. package/src/Link.stories.tsx +5 -4
  65. package/src/Menu.tsx +3 -1
  66. package/src/NavItem.tsx +1 -0
  67. package/src/PersonTrigger.tsx +1 -0
  68. package/src/Reaction.tsx +3 -1
  69. package/src/ReactionPicker.stories.tsx +3 -2
  70. package/src/ScrollArea.stories.tsx +13 -12
  71. package/src/SearchInput.tsx +5 -4
  72. package/src/SectionLabel.mdx +4 -2
  73. package/src/SectionLabel.stories.tsx +13 -0
  74. package/src/SectionLabel.tsx +19 -2
  75. package/src/Select.tsx +6 -4
  76. package/src/TextInput.tsx +6 -6
  77. package/src/Textarea.tsx +6 -4
  78. package/src/Tooltip.mdx +1 -0
  79. package/src/Tooltip.stories.tsx +3 -2
  80. package/src/Tooltip.tsx +2 -1
  81. package/src/eslint/index.test.ts +16 -9
  82. package/src/eslint/index.ts +19 -6
  83. package/src/eslint/looks-of.ts +302 -0
  84. package/src/eslint/no-copied-look.test.ts +155 -0
  85. package/src/eslint/no-copied-look.ts +203 -0
  86. package/src/eslint/no-native-title.test.ts +67 -0
  87. package/src/eslint/no-native-title.ts +72 -0
  88. package/src/eslint/no-restyled-part.ts +1 -1
  89. package/src/gates/app-checks.ts +1 -1
  90. package/src/gates/create-app.test.ts +1 -1
  91. package/src/gates/gates.test.ts +1 -1
  92. package/src/looks.ts +42 -0
  93. package/src/registry/app.ts +3 -0
  94. package/src/registry/build.ts +8 -0
  95. package/src/registry/registry.test.ts +1 -1
  96. package/src/registry/schema.ts +9 -0
  97. package/dist/registry/app-FXN4EI34.js +0 -10
  98. package/dist/registry/chunk-E4JNV7PC.js.map +0 -7
  99. package/dist/registry/chunk-NJN4MQAM.js +0 -397
  100. package/dist/registry/chunk-NJN4MQAM.js.map +0 -7
  101. package/dist/registry/chunk-XCBN2EKW.js.map +0 -7
  102. /package/dist/registry/{app-FXN4EI34.js.map → app-3ALZE6XO.js.map} +0 -0
  103. /package/dist/registry/{build-LGFCCOLR.js.map → build-NDXMVFQ5.js.map} +0 -0
@@ -0,0 +1,595 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SCHEMA_VERSION
4
+ } from "./chunk-PZO4BVV3.js";
5
+
6
+ // src/registry/build.ts
7
+ import { readFileSync as readFileSync2, readdirSync } from "node:fs";
8
+ import { join as join2 } from "node:path";
9
+ import ts2 from "typescript";
10
+ import { OWNED_BEHAVIOURS } from "../eslint/index.js";
11
+
12
+ // src/eslint/looks-of.ts
13
+ import { existsSync, readFileSync } from "node:fs";
14
+ import { dirname, join, resolve as resolvePath } from "node:path";
15
+ import ts from "typescript";
16
+ var PLACEMENT = [
17
+ /^m[xytrblse]?-/,
18
+ /^p[xytrblse]?-/,
19
+ /^(?:w|h|size|min-w|min-h|max-w|max-h)-/,
20
+ /^(?:hidden|block|inline|inline-block|inline-flex|inline-grid|flex|grid|contents|flow-root)$/,
21
+ /^(?: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)-/,
22
+ /^(?:self|justify|justify-items|justify-self|items|place-content|place-items|place-self)-|^content-(?!\[)/,
23
+ /^(?:static|relative|absolute|fixed|sticky)$|^(?:inset|inset-x|inset-y|top|right|bottom|left|start|end|z)-/,
24
+ /^(?:group|peer)(?:\/[\w-]+)?$/
25
+ ];
26
+ var bare = (token) => token.replace(/^(?:[^:\s[\]]+:)+/, "");
27
+ var isPlacementWord = (token) => PLACEMENT.some((pattern) => pattern.test(bare(token)));
28
+ var CLASSY = /^!?(?:[a-z0-9-]+:|\[[^\]\s]+\]:)*-?[a-z@][a-z0-9-]*(?:-\[[^\]\s]+\]|\/[\w.[\]-]+|-[\w.]+)*$|^\[[^\]\s]+\]$/;
29
+ var words = (text) => text.split(/\s+/).filter(Boolean);
30
+ var CLASS_FUNCTIONS = /* @__PURE__ */ new Set(["cn", "clsx", "cx", "twMerge", "classNames"]);
31
+ var CLASS_PROP = /^(?:className|[a-z][A-Za-z]*ClassName)$/;
32
+ function isClassList(text) {
33
+ const list = words(text);
34
+ return list.length >= 3 && list.every((w) => CLASSY.test(w)) && list.filter((w) => /[-:[]/.test(w)).length >= 2;
35
+ }
36
+ var CAP = 24;
37
+ function classUnits(source, follow) {
38
+ const consts = /* @__PURE__ */ new Map();
39
+ const collect = (node) => {
40
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isVariableDeclarationList(node.parent) && node.parent.flags & ts.NodeFlags.Const) {
41
+ consts.set(node.name.text, node.initializer);
42
+ }
43
+ ts.forEachChild(node, collect);
44
+ };
45
+ collect(source);
46
+ const product = (lists) => {
47
+ let out = [[]];
48
+ for (const alternatives of lists) {
49
+ const next = [];
50
+ for (const a of out) for (const b of alternatives) if (next.length < CAP) next.push([...a, ...b]);
51
+ out = next;
52
+ }
53
+ return out;
54
+ };
55
+ const resolve = (node, depth = 0) => {
56
+ if (!node || depth > 8) return [[]];
57
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return [words(node.text)];
58
+ if (ts.isTemplateExpression(node)) return product([[words(node.head.text)], ...node.templateSpans.flatMap((span) => [resolve(span.expression, depth + 1), [words(span.literal.text)]])]);
59
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node)) return resolve(node.expression, depth + 1);
60
+ if (ts.isJsxExpression(node)) return resolve(node.expression, depth + 1);
61
+ if (ts.isCallExpression(node)) return ts.isIdentifier(node.expression) && CLASS_FUNCTIONS.has(node.expression.text) ? product(node.arguments.map((a) => resolve(a, depth + 1))) : [[]];
62
+ if (ts.isConditionalExpression(node)) return [...resolve(node.whenTrue, depth + 1), ...resolve(node.whenFalse, depth + 1)].slice(0, CAP);
63
+ if (ts.isBinaryExpression(node)) {
64
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) return [[], ...resolve(node.right, depth + 1)];
65
+ if (node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) return [...resolve(node.left, depth + 1), ...resolve(node.right, depth + 1)].slice(0, CAP);
66
+ return [[]];
67
+ }
68
+ if (ts.isArrayLiteralExpression(node)) return product(node.elements.map((e) => resolve(e, depth + 1)));
69
+ if (ts.isObjectLiteralExpression(node)) return [[], node.properties.flatMap((p) => p.name && (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) ? words(p.name.text) : [])];
70
+ if (ts.isIdentifier(node)) {
71
+ const init = consts.get(node.text) ?? follow?.(node.text);
72
+ return init ? resolve(init, depth + 1) : [[]];
73
+ }
74
+ if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
75
+ let target = node.expression;
76
+ if (ts.isIdentifier(target)) target = consts.get(target.text) ?? follow?.(target.text);
77
+ while (target && (ts.isAsExpression(target) || ts.isSatisfiesExpression(target) || ts.isParenthesizedExpression(target))) target = target.expression;
78
+ if (!target || !ts.isObjectLiteralExpression(target)) return [[]];
79
+ const key = ts.isPropertyAccessExpression(node) ? node.name.text : ts.isStringLiteral(node.argumentExpression) ? node.argumentExpression.text : null;
80
+ return target.properties.filter((p) => ts.isPropertyAssignment(p) && (key === null || !!p.name && (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) && p.name.text === key)).flatMap((p) => resolve(p.initializer, depth + 1)).slice(0, CAP);
81
+ }
82
+ return [[]];
83
+ };
84
+ const units = [];
85
+ const seen = /* @__PURE__ */ new Set();
86
+ const sharedAt = (node) => {
87
+ for (let at = node; at; at = at.parent) {
88
+ if (ts.isVariableDeclaration(at)) return ts.isIdentifier(at.name) && /_CLASSES$/.test(at.name.text) && !!at.parent?.parent && ts.isVariableStatement(at.parent.parent) && !!at.parent.parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
89
+ }
90
+ return false;
91
+ };
92
+ const lineStart = (node) => {
93
+ const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line;
94
+ let at = node;
95
+ while (at.parent && !ts.isSourceFile(at.parent) && source.getLineAndCharacterOfPosition(at.parent.getStart(source)).line === line) at = at.parent;
96
+ return at.getStart(source);
97
+ };
98
+ const add = (tokens, node, via, anchor = via === "literal" ? lineStart(node) : node.getStart(source)) => {
99
+ const list = [...new Set(tokens.filter((t) => CLASSY.test(t)))];
100
+ const look = list.filter((t) => !isPlacementWord(t));
101
+ if (look.length === 0) return;
102
+ const key = `${via}:${node.getStart(source)}:${[...list].sort().join(" ")}`;
103
+ if (seen.has(key)) return;
104
+ seen.add(key);
105
+ units.push({ tokens: list, look, start: node.getStart(source), end: node.getEnd(), via, anchor, shared: via === "literal" && sharedAt(node) });
106
+ };
107
+ const visit = (node) => {
108
+ if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && CLASS_PROP.test(node.name.text) && node.initializer) {
109
+ const element = node.parent.parent;
110
+ for (const alternative of resolve(node.initializer)) add(alternative, node, "attribute", element.getStart(source));
111
+ }
112
+ if ((ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) && isClassList(node.text)) add(words(node.text), node, "literal");
113
+ ts.forEachChild(node, visit);
114
+ };
115
+ visit(source);
116
+ return units;
117
+ }
118
+ function componentsOf(source) {
119
+ const found = /* @__PURE__ */ new Map();
120
+ const fn = (node) => {
121
+ let current = node;
122
+ while (current && ts.isCallExpression(current) && current.arguments.length) current = current.arguments[0];
123
+ return current && (ts.isArrowFunction(current) || ts.isFunctionExpression(current)) ? current : void 0;
124
+ };
125
+ for (const statement of source.statements) {
126
+ if (ts.isFunctionDeclaration(statement) && statement.name && /^[A-Z]/.test(statement.name.text)) found.set(statement.name.text, { start: statement.getStart(source), end: statement.getEnd() });
127
+ if (ts.isVariableStatement(statement)) {
128
+ for (const declaration of statement.declarationList.declarations) {
129
+ const body = fn(declaration.initializer);
130
+ if (ts.isIdentifier(declaration.name) && /^[A-Z]/.test(declaration.name.text) && body) found.set(declaration.name.text, { start: body.getStart(source), end: body.getEnd() });
131
+ }
132
+ }
133
+ }
134
+ return found;
135
+ }
136
+ var MIN_LOOK_WORDS = 4;
137
+ function looksOf(source, names) {
138
+ const components = componentsOf(source);
139
+ const units = classUnits(source, importedConstants(source)).filter((u) => u.look.length >= MIN_LOOK_WORDS);
140
+ const inside = (u, r) => u.start >= r.start && u.start < r.end;
141
+ const out = /* @__PURE__ */ new Map();
142
+ for (const name of names ?? components.keys()) {
143
+ const range = components.get(name);
144
+ const mine = units.filter((u) => (range ? inside(u, range) : false) || ![...components.values()].some((r) => inside(u, r)));
145
+ out.set(name, [...new Set(mine.map((u) => u.look.join(" ")))]);
146
+ }
147
+ return out;
148
+ }
149
+ function parseForLooks(fileName, text) {
150
+ return ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true, fileName.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
151
+ }
152
+ function importTarget(fromFile, specifier) {
153
+ let base;
154
+ if (specifier.startsWith(".")) base = resolvePath(dirname(fromFile), specifier);
155
+ else if (specifier.startsWith("@/")) {
156
+ let dir = dirname(resolvePath(fromFile));
157
+ while (!existsSync(join(dir, "package.json")) && dirname(dir) !== dir) dir = dirname(dir);
158
+ base = join(dir, "src", specifier.slice(2));
159
+ } else return null;
160
+ for (const candidate of [`${base}.ts`, `${base}.tsx`, join(base, "index.ts")]) if (existsSync(candidate)) return candidate;
161
+ return null;
162
+ }
163
+ var constantsCache = /* @__PURE__ */ new Map();
164
+ function constantsOf(file) {
165
+ const cached = constantsCache.get(file);
166
+ if (cached) return cached;
167
+ const found = /* @__PURE__ */ new Map();
168
+ const source = parseForLooks(file, readFileSync(file, "utf8"));
169
+ for (const statement of source.statements) {
170
+ if (!ts.isVariableStatement(statement)) continue;
171
+ for (const d of statement.declarationList.declarations) if (ts.isIdentifier(d.name) && d.initializer) found.set(d.name.text, d.initializer);
172
+ }
173
+ constantsCache.set(file, found);
174
+ return found;
175
+ }
176
+ function importedConstants(source) {
177
+ const imported = /* @__PURE__ */ new Map();
178
+ for (const statement of source.statements) {
179
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
180
+ const bindings = statement.importClause?.namedBindings;
181
+ if (!bindings || !ts.isNamedImports(bindings)) continue;
182
+ const target = importTarget(source.fileName, statement.moduleSpecifier.text);
183
+ if (!target) continue;
184
+ for (const element of bindings.elements) {
185
+ const local = element.name.text;
186
+ if (/_CLASSES$/.test(element.propertyName?.text ?? local)) imported.set(local, { file: target, name: element.propertyName?.text ?? local });
187
+ }
188
+ }
189
+ return (name) => {
190
+ const from = imported.get(name);
191
+ if (!from) return void 0;
192
+ try {
193
+ return constantsOf(from.file).get(from.name);
194
+ } catch {
195
+ return void 0;
196
+ }
197
+ };
198
+ }
199
+
200
+ // src/registry/build.ts
201
+ var PACKAGE_IMPORT = "@estiva-app/ui";
202
+ function sanitize(name) {
203
+ return name.toLowerCase().replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>{}[\]\\/]/g, "-").replace(/-+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
204
+ }
205
+ function storyNameFromExport(key) {
206
+ return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-zA-Z])([0-9])/g, "$1 $2").replace(/([0-9])([a-zA-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
207
+ }
208
+ var toId = (title, name) => `${sanitize(title)}--${sanitize(storyNameFromExport(name))}`;
209
+ function readIndexExports(source) {
210
+ const file = ts2.createSourceFile("index.ts", source, ts2.ScriptTarget.Latest, true);
211
+ const found = [];
212
+ for (const statement of file.statements) {
213
+ if (!ts2.isExportDeclaration(statement)) continue;
214
+ const from = statement.moduleSpecifier;
215
+ const clause = statement.exportClause;
216
+ if (!from || !ts2.isStringLiteral(from) || !clause || !ts2.isNamedExports(clause)) continue;
217
+ for (const element of clause.elements) {
218
+ found.push({ name: element.name.text, module: from.text.replace(/^\.\//, ""), isType: statement.isTypeOnly || element.isTypeOnly });
219
+ }
220
+ }
221
+ return found;
222
+ }
223
+ function docAbove(source, node, headerPos = null) {
224
+ const ranges = ts2.getLeadingCommentRanges(source, node.getFullStart()) ?? [];
225
+ const jsdoc = ranges.filter((range) => source.slice(range.pos, range.pos + 3) === "/**").pop();
226
+ if (!jsdoc) return "";
227
+ if (headerPos !== null && jsdoc.pos === headerPos) return "";
228
+ const between = source.slice(jsdoc.end, node.getStart());
229
+ if (between.replace(/\/\/[^\n]*/g, "").trim() !== "") return "";
230
+ return source.slice(jsdoc.pos, jsdoc.end).split(/\r?\n/).map((line) => line.trim().replace(/^\/\*\*/, "").replace(/\*\/$/, "").replace(/^\*/, "").trim()).join("\n").trim();
231
+ }
232
+ function firstSentence(prose) {
233
+ const flat = prose.split(/\n\s*\n/)[0].split("\n").filter((line2) => !line2.trim().startsWith("@")).join(" ").replace(/\*\*/g, "").replace(/\s+/g, " ").trim();
234
+ const parts = flat.split(/(?<=[.!?])\s+(?=[A-Z`'"‘“])/);
235
+ let line = parts[0] ?? "";
236
+ if (line.length < 40 && parts[1]) line = `${line} ${parts[1]}`;
237
+ return line.trim();
238
+ }
239
+ function pageOpening(mdx) {
240
+ const lines = mdx.split(/\r?\n/);
241
+ const heading = lines.findIndex((line) => /^#\s+\S/.test(line));
242
+ if (heading < 0) return "";
243
+ const paragraph = [];
244
+ for (const line of lines.slice(heading + 1)) {
245
+ const text = line.trim();
246
+ if (text === "") {
247
+ if (paragraph.length) break;
248
+ continue;
249
+ }
250
+ if (text.startsWith("<") || text.startsWith("#")) break;
251
+ paragraph.push(text);
252
+ }
253
+ return paragraph.join(" ");
254
+ }
255
+ function propName(member) {
256
+ if (!member.name) return null;
257
+ if (ts2.isIdentifier(member.name) || ts2.isStringLiteral(member.name)) return member.name.text;
258
+ return null;
259
+ }
260
+ function literalUnion(type, aliases, seen = /* @__PURE__ */ new Set()) {
261
+ if (!type) return null;
262
+ if (ts2.isTypeReferenceNode(type) && ts2.isIdentifier(type.typeName)) {
263
+ const name = type.typeName.text;
264
+ if (seen.has(name)) return null;
265
+ seen.add(name);
266
+ const alias = aliases.get(name);
267
+ return alias ? literalUnion(alias, aliases, seen) : null;
268
+ }
269
+ if (ts2.isLiteralTypeNode(type) && ts2.isStringLiteral(type.literal)) return [type.literal.text];
270
+ if (!ts2.isUnionTypeNode(type)) return null;
271
+ const values = [];
272
+ for (const member of type.types) {
273
+ if (member.kind === ts2.SyntaxKind.UndefinedKeyword || member.kind === ts2.SyntaxKind.NullKeyword) continue;
274
+ const inner = literalUnion(member, aliases, seen);
275
+ if (!inner) return null;
276
+ values.push(...inner);
277
+ }
278
+ return values.length > 1 ? values : null;
279
+ }
280
+ function readModule(source, sibling = () => void 0, { lendFirstComment = true } = {}) {
281
+ const file = ts2.createSourceFile("module.tsx", source, ts2.ScriptTarget.Latest, true);
282
+ const declarations = /* @__PURE__ */ new Map();
283
+ const aliases = /* @__PURE__ */ new Map();
284
+ const shapes = /* @__PURE__ */ new Map();
285
+ const bases = /* @__PURE__ */ new Map();
286
+ const importedFrom = /* @__PURE__ */ new Map();
287
+ const propsTypeOf = (parameters) => parameters[0]?.type;
288
+ const propsOfInitializer = (initializer) => {
289
+ if (!initializer) return void 0;
290
+ if (ts2.isArrowFunction(initializer) || ts2.isFunctionExpression(initializer)) return propsTypeOf(initializer.parameters);
291
+ if (ts2.isCallExpression(initializer)) {
292
+ const callee = ts2.isPropertyAccessExpression(initializer.expression) ? initializer.expression.name.text : ts2.isIdentifier(initializer.expression) ? initializer.expression.text : "";
293
+ if (callee === "forwardRef" && initializer.typeArguments?.[1]) return initializer.typeArguments[1];
294
+ const wrapped = initializer.arguments.find((argument) => ts2.isArrowFunction(argument) || ts2.isFunctionExpression(argument));
295
+ if (wrapped) return propsTypeOf(wrapped.parameters);
296
+ }
297
+ return void 0;
298
+ };
299
+ const propsOfAnnotation = (type) => {
300
+ if (!type || !ts2.isTypeReferenceNode(type)) return void 0;
301
+ const named = ts2.isIdentifier(type.typeName) ? type.typeName.text : type.typeName.right.text;
302
+ return named === "FC" || named === "FunctionComponent" ? type.typeArguments?.[0] : void 0;
303
+ };
304
+ const opening = file.statements.find((statement) => !ts2.isImportDeclaration(statement));
305
+ const headerPos = opening ? (ts2.getLeadingCommentRanges(source, opening.getFullStart()) ?? []).find((range) => source.slice(range.pos, range.pos + 3) === "/**")?.pos ?? null : null;
306
+ const topPos = file.statements[0] ? (ts2.getLeadingCommentRanges(source, file.statements[0].getFullStart()) ?? []).find((range) => source.slice(range.pos, range.pos + 3) === "/**")?.pos ?? null : null;
307
+ const aboveImports = !!file.statements[0] && ts2.isImportDeclaration(file.statements[0]);
308
+ const refused = lendFirstComment ? topPos ?? headerPos : aboveImports ? topPos : null;
309
+ for (const statement of file.statements) {
310
+ if (ts2.isImportDeclaration(statement) && ts2.isStringLiteral(statement.moduleSpecifier)) {
311
+ const bindings = statement.importClause?.namedBindings;
312
+ if (bindings && ts2.isNamedImports(bindings)) {
313
+ for (const element of bindings.elements) importedFrom.set(element.name.text, { specifier: statement.moduleSpecifier.text, imported: (element.propertyName ?? element.name).text });
314
+ }
315
+ }
316
+ if (ts2.isExportDeclaration(statement) && statement.moduleSpecifier && ts2.isStringLiteral(statement.moduleSpecifier) && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
317
+ for (const element of statement.exportClause.elements) importedFrom.set(element.name.text, { specifier: statement.moduleSpecifier.text, imported: (element.propertyName ?? element.name).text });
318
+ }
319
+ if (ts2.isTypeAliasDeclaration(statement)) aliases.set(statement.name.text, statement.type);
320
+ if (ts2.isInterfaceDeclaration(statement)) {
321
+ shapes.set(statement.name.text, [...statement.members]);
322
+ const extended = (statement.heritageClauses ?? []).flatMap((clause) => [...clause.types]);
323
+ if (extended.length) bases.set(statement.name.text, extended);
324
+ }
325
+ if (ts2.isTypeAliasDeclaration(statement) && ts2.isTypeLiteralNode(statement.type)) shapes.set(statement.name.text, [...statement.type.members]);
326
+ if (ts2.isClassDeclaration(statement)) {
327
+ const base = (statement.heritageClauses ?? []).find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword)?.types[0];
328
+ const name = statement.name?.text ?? (ts2.getModifiers(statement)?.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword) ? "default" : null);
329
+ if (name) declarations.set(name, { doc: docAbove(source, statement, refused), deprecated: false, propsType: base?.typeArguments?.[0] });
330
+ continue;
331
+ }
332
+ if (ts2.isExportAssignment(statement) && !statement.isExportEquals && !ts2.isIdentifier(statement.expression)) {
333
+ declarations.set("default", { doc: docAbove(source, statement, refused), deprecated: false, propsType: propsOfInitializer(statement.expression) });
334
+ continue;
335
+ }
336
+ if (ts2.isFunctionDeclaration(statement) && !statement.name && ts2.getModifiers(statement)?.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword)) {
337
+ declarations.set("default", { doc: docAbove(source, statement, refused), deprecated: false, propsType: propsTypeOf(statement.parameters) });
338
+ continue;
339
+ }
340
+ if (!ts2.isFunctionDeclaration(statement) && !ts2.isVariableStatement(statement)) continue;
341
+ const doc = docAbove(source, statement, refused);
342
+ const deprecated = /(^|\n)@deprecated\b/.test(doc) || /\*\s*@deprecated\b/.test(source.slice(Math.max(0, statement.getFullStart()), statement.getStart()));
343
+ if (ts2.isFunctionDeclaration(statement) && statement.name) {
344
+ const held = declarations.get(statement.name.text);
345
+ if (held) {
346
+ if (!held.doc && doc) held.doc = doc;
347
+ continue;
348
+ }
349
+ declarations.set(statement.name.text, { doc, deprecated, propsType: propsTypeOf(statement.parameters) });
350
+ continue;
351
+ }
352
+ if (ts2.isVariableStatement(statement)) {
353
+ for (const declaration of statement.declarationList.declarations) {
354
+ if (!ts2.isIdentifier(declaration.name)) continue;
355
+ declarations.set(declaration.name.text, { doc, deprecated, propsType: propsOfInitializer(declaration.initializer) ?? propsOfAnnotation(declaration.type) });
356
+ }
357
+ }
358
+ }
359
+ const EMPTY = { props: [], variants: [] };
360
+ const keysOf = (type) => {
361
+ if (!type) return null;
362
+ if (ts2.isLiteralTypeNode(type) && ts2.isStringLiteral(type.literal)) return [type.literal.text];
363
+ if (!ts2.isUnionTypeNode(type)) return null;
364
+ const keys = [];
365
+ for (const member of type.types) {
366
+ if (!ts2.isLiteralTypeNode(member) || !ts2.isStringLiteral(member.literal)) return null;
367
+ keys.push(member.literal.text);
368
+ }
369
+ return keys;
370
+ };
371
+ const narrow = (resolved, how, keys) => {
372
+ if (!keys) return resolved;
373
+ const named = new Set(keys);
374
+ const keep = (name) => how === "Omit" ? !named.has(name) : named.has(name);
375
+ return { props: resolved.props.filter((prop) => keep(prop.name)), variants: resolved.variants.filter((variant) => keep(variant.prop)) };
376
+ };
377
+ const merge = (parts) => {
378
+ const props = [];
379
+ const variants = [];
380
+ const seenProp = /* @__PURE__ */ new Set();
381
+ const seenVariant = /* @__PURE__ */ new Set();
382
+ for (const part of parts) {
383
+ for (const prop of part.props) if (!seenProp.has(prop.name)) seenProp.add(prop.name), props.push(prop);
384
+ for (const variant of part.variants) if (!seenVariant.has(variant.prop)) seenVariant.add(variant.prop), variants.push(variant);
385
+ }
386
+ return { props, variants };
387
+ };
388
+ const takes = (type, values) => {
389
+ if (values) return values.join(" | ");
390
+ if (!type) return "anything";
391
+ const written = type.getText(file).replace(/\s+/g, " ").trim();
392
+ if (written === "boolean") return "true/false";
393
+ if (written === "number") return "number";
394
+ if (written === "string") return "text";
395
+ if (/^React(Node|Element)\b/.test(written)) return "anything";
396
+ if (written.includes("=>")) return "a handler";
397
+ return written.length > 60 ? `${written.slice(0, 57)}\u2026` : written;
398
+ };
399
+ const fromMembers = (members) => {
400
+ const props = [];
401
+ const variants = [];
402
+ for (const member of members) {
403
+ const name = propName(member);
404
+ if (name === null || !ts2.isPropertySignature(member)) continue;
405
+ const values = literalUnion(member.type, aliases);
406
+ const note = firstSentence(docAbove(source, member));
407
+ props.push({ name, takes: takes(member.type, values), required: !member.questionToken, note: note || null });
408
+ if (values) variants.push({ prop: name, values });
409
+ }
410
+ return { props, variants };
411
+ };
412
+ const resolveNode = (type, seen) => {
413
+ if (!type || seen.size > 12) return EMPTY;
414
+ if (ts2.isTypeLiteralNode(type)) return fromMembers(type.members);
415
+ if (ts2.isIntersectionTypeNode(type)) return merge(type.types.map((side) => resolveNode(side, seen)));
416
+ if (ts2.isTypeReferenceNode(type) && ts2.isIdentifier(type.typeName)) {
417
+ const named = type.typeName.text;
418
+ if (named === "Omit" || named === "Pick") {
419
+ return narrow(resolveNode(type.typeArguments?.[0], seen), named, keysOf(type.typeArguments?.[1]));
420
+ }
421
+ return resolveName(named, seen);
422
+ }
423
+ return EMPTY;
424
+ };
425
+ const resolveBase = (base, seen) => {
426
+ if (!ts2.isIdentifier(base.expression)) return EMPTY;
427
+ const named = base.expression.text;
428
+ if (named === "Omit" || named === "Pick") {
429
+ return narrow(resolveNode(base.typeArguments?.[0], seen), named, keysOf(base.typeArguments?.[1]));
430
+ }
431
+ return resolveName(named, seen);
432
+ };
433
+ function resolveName(name, seen) {
434
+ if (seen.has(name) || seen.size > 12) return EMPTY;
435
+ seen.add(name);
436
+ const own = shapes.get(name);
437
+ if (own) {
438
+ const inherited = (bases.get(name) ?? []).map((base) => resolveBase(base, seen));
439
+ return merge([fromMembers(own), ...inherited]);
440
+ }
441
+ const alias = aliases.get(name);
442
+ if (alias) return resolveNode(alias, seen);
443
+ const from = importedFrom.get(name);
444
+ return (from ? sibling(from.specifier, from.imported) : void 0) ?? EMPTY;
445
+ }
446
+ const resolve = (propsType) => resolveNode(propsType, /* @__PURE__ */ new Set());
447
+ const jsdocAt = (node) => node ? (ts2.getLeadingCommentRanges(source, node.getFullStart()) ?? []).find((range) => source.slice(range.pos, range.pos + 3) === "/**") : void 0;
448
+ const headerRange = jsdocAt(file.statements[0]) ?? jsdocAt(opening);
449
+ return { declarations, headerDoc: headerRange ? headerComment(source, headerRange) : "", resolve, resolveName: (name) => resolveName(name, /* @__PURE__ */ new Set()) };
450
+ }
451
+ function headerComment(source, range) {
452
+ return source.slice(range.pos, range.end).split(/\r?\n/).map((line) => line.trim().replace(/^\/\*\*/, "").replace(/\*\/$/, "").replace(/^\*/, "").trim()).join("\n").trim();
453
+ }
454
+ function readStories(source) {
455
+ const file = ts2.createSourceFile("x.stories.tsx", source, ts2.ScriptTarget.Latest, true);
456
+ let title = null;
457
+ const stories = [];
458
+ for (const statement of file.statements) {
459
+ if (ts2.isVariableStatement(statement)) {
460
+ for (const declaration of statement.declarationList.declarations) {
461
+ if (!ts2.isIdentifier(declaration.name)) continue;
462
+ const initializer = declaration.initializer;
463
+ const object = initializer && ts2.isSatisfiesExpression(initializer) ? initializer.expression : initializer;
464
+ if (declaration.name.text === "meta" && object && ts2.isObjectLiteralExpression(object)) {
465
+ for (const property of object.properties) {
466
+ if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && property.name.text === "title" && ts2.isStringLiteral(property.initializer)) {
467
+ title = property.initializer.text;
468
+ }
469
+ }
470
+ }
471
+ const exported = ts2.getModifiers(statement)?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword);
472
+ if (exported && declaration.name.text !== "meta") stories.push(declaration.name.text);
473
+ }
474
+ }
475
+ }
476
+ return { title, stories };
477
+ }
478
+ var isAllCaps = (name) => /^[A-Z0-9_]+$/.test(name);
479
+ function kindOf(name) {
480
+ if (/^use[A-Z]/.test(name)) return "hook";
481
+ return /^[A-Z]/.test(name) && !isAllCaps(name) ? "component" : "helper";
482
+ }
483
+ function behavioursOf(name) {
484
+ return OWNED_BEHAVIOURS.filter((owned) => owned.owners.includes(name)).map((owned) => ({ id: owned.id, behaviour: owned.behaviour }));
485
+ }
486
+ function buildRegistry({ root = process.cwd(), repo = "estiva-ui" } = {}) {
487
+ const src = join2(root, "src");
488
+ const manifest = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
489
+ const files = new Set(readdirSync(src));
490
+ const exported = readIndexExports(readFileSync2(join2(src, "index.ts"), "utf8"));
491
+ const values = exported.filter((entry) => !entry.isType);
492
+ const modules = /* @__PURE__ */ new Map();
493
+ const moduleOf = (name) => {
494
+ const held = modules.get(name);
495
+ if (held) return held;
496
+ const file = [".tsx", ".ts"].map((extension) => `${name}${extension}`).find((candidate) => files.has(candidate));
497
+ if (!file) throw new Error(`src/index.ts exports from './${name}', which is not a file in src/`);
498
+ const read = { ...readModule(readFileSync2(join2(src, file), "utf8"), (specifier, typeName) => specifier.startsWith("./") ? moduleOf(specifier.slice(2)).resolveName(typeName) : void 0), file: `src/${file}` };
499
+ modules.set(name, read);
500
+ return read;
501
+ };
502
+ const problems = [];
503
+ const entries = [];
504
+ const looksCache = /* @__PURE__ */ new Map();
505
+ const looksFor = (file, name) => {
506
+ if (!looksCache.has(file)) looksCache.set(file, parseForLooks(join2(src, file.replace(/^src\//, "")), readFileSync2(join2(src, file.replace(/^src\//, "")), "utf8")));
507
+ return looksOf(looksCache.get(file), [name]).get(name) ?? [];
508
+ };
509
+ for (const value of values) {
510
+ const module = moduleOf(value.module);
511
+ const declared = module.declarations.get(value.name);
512
+ if (!declared) {
513
+ problems.push(`${value.name} is exported from './${value.module}' but is not declared there`);
514
+ continue;
515
+ }
516
+ const pageFile = files.has(`${value.name}.mdx`) ? `src/${value.name}.mdx` : null;
517
+ const fromPage = pageFile ? firstSentence(pageOpening(readFileSync2(join2(root, pageFile), "utf8"))) : "";
518
+ const fromComment = firstSentence(declared.doc);
519
+ const purpose = fromPage || fromComment;
520
+ if (!purpose) {
521
+ problems.push(`${value.name} has no purpose: no opening paragraph on its page, and no doc comment above it in ${module.file}`);
522
+ continue;
523
+ }
524
+ const storiesFile = files.has(`${value.name}.stories.tsx`) ? `${value.name}.stories.tsx` : files.has(`${value.module}.stories.tsx`) ? `${value.module}.stories.tsx` : null;
525
+ const stories = storiesFile ? readStories(readFileSync2(join2(src, storiesFile), "utf8")) : { title: null, stories: [] };
526
+ const title = stories.title;
527
+ const story = stories.stories.includes(value.name) ? value.name : stories.stories[0];
528
+ const shape = module.resolve(declared.propsType);
529
+ entries.push({
530
+ name: value.name,
531
+ repo,
532
+ kind: kindOf(value.name),
533
+ importPath: PACKAGE_IMPORT,
534
+ sourceFile: module.file,
535
+ purpose,
536
+ purposeFrom: fromPage ? "page" : "comment",
537
+ props: shape.props,
538
+ variants: shape.variants,
539
+ ownsBehaviours: behavioursOf(value.name),
540
+ status: declared.deprecated ? "deprecated" : "stable",
541
+ migrationStage: null,
542
+ docsId: title ? `${sanitize(title)}--docs` : null,
543
+ storyId: title && story ? toId(title, story) : null,
544
+ docPage: pageFile,
545
+ app: null,
546
+ looks: kindOf(value.name) === "component" ? looksFor(module.file, value.name) : []
547
+ });
548
+ }
549
+ if (problems.length) throw new Error(`the registry cannot be built:
550
+ ${problems.join("\n ")}`);
551
+ entries.sort((a, b) => a.name.localeCompare(b.name));
552
+ return {
553
+ schemaVersion: SCHEMA_VERSION,
554
+ builtFrom: {
555
+ kind: "package",
556
+ repo,
557
+ package: manifest.name,
558
+ packageVersion: manifest.version,
559
+ exports: values.length,
560
+ typeExports: exported.length - values.length,
561
+ files: null
562
+ },
563
+ storybook: {
564
+ docsPath: "/?path=/docs/{docsId}",
565
+ storyPath: "/?path=/story/{storyId}",
566
+ devUrl: "http://localhost:6008"
567
+ },
568
+ entries,
569
+ // Nothing is excluded: every value export is an entry, helpers and the one
570
+ // hook included, each marked by `kind`. The field stays because the count
571
+ // has to reconcile out loud — see validateRegistry.
572
+ excluded: [],
573
+ filesWithoutParts: []
574
+ };
575
+ }
576
+ function serializeRegistry(registry) {
577
+ return `${JSON.stringify(registry, null, 2)}
578
+ `;
579
+ }
580
+
581
+ export {
582
+ looksOf,
583
+ parseForLooks,
584
+ PACKAGE_IMPORT,
585
+ sanitize,
586
+ toId,
587
+ readIndexExports,
588
+ firstSentence,
589
+ pageOpening,
590
+ readModule,
591
+ readStories,
592
+ buildRegistry,
593
+ serializeRegistry
594
+ };
595
+ //# sourceMappingURL=chunk-XVII7P6G.js.map