@genesislcap/ts-builder 14.418.2 → 14.419.1

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.
@@ -0,0 +1,704 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateReactWrappers = generateReactWrappers;
4
+ const tslib_1 = require("tslib");
5
+ const node_fs_1 = require("node:fs");
6
+ const promises_1 = require("node:fs/promises");
7
+ const node_path_1 = require("node:path");
8
+ // ── Constants ────────────────────────────────────────────────────────────────
9
+ const PRIMITIVE_UNION_REGEX = /^(?:\s*(?:string|number|boolean|bigint|null|undefined|unknown|any|void|'[^']*'|"[^"]*"|`[^`]*`|(?:\d+(?:\.\d+)?))\s*)(?:\|\s*(?:string|number|boolean|bigint|null|undefined|unknown|any|void|'[^']*'|"[^"]*"|`[^`]*`|(?:\d+(?:\.\d+)?))\s*)*$/;
10
+ const IDENTIFIER_TOKEN_REGEX = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
11
+ const PRIMITIVE_TOKENS = new Set([
12
+ 'true', 'false', 'null', 'undefined', 'string', 'number', 'boolean',
13
+ 'bigint', 'symbol', 'unknown', 'any', 'void', 'never',
14
+ ]);
15
+ const KNOWN_TYPE_NAMES = new Set([
16
+ 'Array', 'ReadonlyArray', 'Promise', 'Record', 'Partial', 'Required', 'Pick', 'Omit',
17
+ 'Map', 'Set', 'WeakMap', 'WeakSet', 'Date', 'RegExp', 'Error', 'Node', 'Element',
18
+ 'HTMLElement', 'SVGElement', 'Event', 'CustomEvent', 'MouseEvent', 'KeyboardEvent',
19
+ 'FocusEvent', 'InputEvent', 'PointerEvent', 'WheelEvent', 'DragEvent', 'SubmitEvent',
20
+ 'AbortSignal', 'DOMRect', 'Document', 'Window', 'URL', 'URLSearchParams',
21
+ 'CSSStyleDeclaration', 'Intl.Locale',
22
+ ]);
23
+ /**
24
+ * DOM event classes that map directly to `(event: T) => void`.
25
+ * Excludes bare `CustomEvent` which needs special handling for its detail type.
26
+ */
27
+ const DOM_EVENT_CLASS_NAMES = new Set([
28
+ 'Event', 'MouseEvent', 'KeyboardEvent', 'FocusEvent', 'InputEvent',
29
+ 'PointerEvent', 'WheelEvent', 'DragEvent', 'SubmitEvent',
30
+ ]);
31
+ /**
32
+ * React DOM reserves these `on*` prop names for native/synthetic events. When a CEM event
33
+ * name maps to the same handler (e.g. `click` → `onClick`), we must not emit a duplicate
34
+ * wrapper prop or it clashes with React's built-in typings.
35
+ *
36
+ * Resolved at startup from the consumer project's `@types/react/index.d.ts` so the set stays
37
+ * current without manual maintenance. Falls back to a static snapshot when `@types/react` is
38
+ * not installed (e.g. JS-only consumers — in that case the exclusion set is irrelevant anyway).
39
+ */
40
+ function loadReactEventHandlerNames() {
41
+ try {
42
+ const typesPath = require.resolve('@types/react/index.d.ts');
43
+ const content = (0, node_fs_1.readFileSync)(typesPath, 'utf-8');
44
+ const names = new Set();
45
+ for (const m of content.matchAll(/\b(on[A-Z][a-zA-Z]+)\??\s*:/g)) {
46
+ names.add(m[1]);
47
+ }
48
+ if (names.size > 20)
49
+ return names;
50
+ }
51
+ catch (_a) { }
52
+ // Static snapshot — kept as fallback only.
53
+ return new Set([
54
+ 'onCopy', 'onCut', 'onPaste', 'onCompositionEnd', 'onCompositionStart', 'onCompositionUpdate',
55
+ 'onFocus', 'onBlur', 'onChange', 'onBeforeInput', 'onInput', 'onReset', 'onSubmit',
56
+ 'onInvalid', 'onLoad', 'onError', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onAbort',
57
+ 'onCanPlay', 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', 'onEnded',
58
+ 'onLoadedData', 'onLoadedMetadata', 'onLoadStart', 'onPause', 'onPlay', 'onPlaying',
59
+ 'onProgress', 'onRateChange', 'onResize', 'onSeeked', 'onSeeking', 'onStalled', 'onSuspend',
60
+ 'onTimeUpdate', 'onVolumeChange', 'onWaiting', 'onAuxClick', 'onClick', 'onContextMenu',
61
+ 'onDoubleClick', 'onDrag', 'onDragEnd', 'onDragEnter', 'onDragExit', 'onDragLeave',
62
+ 'onDragOver', 'onDragStart', 'onDrop', 'onMouseDown', 'onMouseEnter', 'onMouseLeave',
63
+ 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', 'onSelect', 'onTouchCancel',
64
+ 'onTouchEnd', 'onTouchMove', 'onTouchStart', 'onPointerOver', 'onPointerEnter',
65
+ 'onPointerDown', 'onPointerMove', 'onPointerUp', 'onPointerCancel', 'onPointerOut',
66
+ 'onPointerLeave', 'onGotPointerCapture', 'onLostPointerCapture', 'onScroll', 'onWheel',
67
+ 'onAnimationStart', 'onAnimationEnd', 'onAnimationIteration', 'onTransitionEnd', 'onToggle',
68
+ ]);
69
+ }
70
+ const REACT_NATIVE_EVENT_HANDLER_NAMES = loadReactEventHandlerNames();
71
+ /**
72
+ * Emitted into every react.d.ts.
73
+ * onChange/onInput use method signatures for bivariant parameter checking so both
74
+ * native Event and CustomEvent callbacks are accepted without cast.
75
+ */
76
+ const HELPER_TYPES = `\
77
+ /** @internal Maps a web component class to its public props only.
78
+ * keyof T skips private/protected members, so this avoids the TS error
79
+ * "property may not be private or protected" on exported anonymous types. */
80
+ type PublicOf<T> = { [K in keyof T]?: T[K] };
81
+
82
+ /** @internal Safe React HTML attributes for web component wrappers.
83
+ * onChange/onInput use method signatures for bivariant parameter checking so both
84
+ * native Event and CustomEvent callbacks are accepted. */
85
+ interface HTMLWCProps extends React.AriaAttributes {
86
+ className?: string; style?: React.CSSProperties; id?: string; slot?: string;
87
+ tabIndex?: number; dir?: string; lang?: string; title?: string;
88
+ onClick?: React.MouseEventHandler<HTMLElement>;
89
+ onDoubleClick?: React.MouseEventHandler<HTMLElement>;
90
+ onContextMenu?: React.MouseEventHandler<HTMLElement>;
91
+ onMouseEnter?: React.MouseEventHandler<HTMLElement>;
92
+ onMouseLeave?: React.MouseEventHandler<HTMLElement>;
93
+ onMouseDown?: React.MouseEventHandler<HTMLElement>;
94
+ onMouseUp?: React.MouseEventHandler<HTMLElement>;
95
+ onMouseMove?: React.MouseEventHandler<HTMLElement>;
96
+ onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
97
+ onKeyUp?: React.KeyboardEventHandler<HTMLElement>;
98
+ onFocus?: React.FocusEventHandler<HTMLElement>;
99
+ onBlur?: React.FocusEventHandler<HTMLElement>;
100
+ onScroll?: React.UIEventHandler<HTMLElement>;
101
+ onWheel?: React.WheelEventHandler<HTMLElement>;
102
+ onChange?(e: Event): void;
103
+ onInput?(e: Event): void;
104
+ }
105
+ `;
106
+ // ── String utilities ─────────────────────────────────────────────────────────
107
+ function normalizeWhitespace(value) {
108
+ return value.replace(/\s+/g, ' ').trim();
109
+ }
110
+ function normalizePropertyName(name) {
111
+ let s = name.trim();
112
+ if (!s)
113
+ return null;
114
+ const bracketQuoted = s.match(/^\[\s*['"](.+?)['"]\s*\]$/);
115
+ if (bracketQuoted) {
116
+ s = bracketQuoted[1];
117
+ }
118
+ else {
119
+ const bracket = s.match(/^\[\s*(.+?)\s*\]$/);
120
+ if (bracket)
121
+ s = bracket[1];
122
+ const quoted = s.match(/^['"](.+?)['"]$/);
123
+ if (quoted)
124
+ s = quoted[1];
125
+ }
126
+ s = s.trim();
127
+ if (!s || s.includes('\n') || s.includes('\r'))
128
+ return null;
129
+ return s;
130
+ }
131
+ function toPascalCase(value) {
132
+ return value
133
+ .split(/[^a-zA-Z0-9]+/)
134
+ .filter(Boolean)
135
+ .map((part) => `${part[0].toUpperCase()}${part.slice(1)}`)
136
+ .join('');
137
+ }
138
+ // ── Type resolution utilities ─────────────────────────────────────────────────
139
+ function isPrimitiveToken(token) {
140
+ return PRIMITIVE_TOKENS.has(token);
141
+ }
142
+ function isKnownTypeIdentifier(identifier) {
143
+ return KNOWN_TYPE_NAMES.has(identifier) || identifier.startsWith('globalThis.');
144
+ }
145
+ function getIdentifierTokens(typeText) {
146
+ var _a;
147
+ return (_a = typeText.match(IDENTIFIER_TOKEN_REGEX)) !== null && _a !== void 0 ? _a : [];
148
+ }
149
+ function createTypeImportState() {
150
+ return {
151
+ importsByIdentifier: new Map(),
152
+ ambiguousIdentifiers: new Set(),
153
+ usedImports: new Map(),
154
+ wildcardExportModules: new Set(),
155
+ };
156
+ }
157
+ function registerTypeImport(state, identifier, moduleSpecifier) {
158
+ if (state.ambiguousIdentifiers.has(identifier))
159
+ return;
160
+ const existing = state.importsByIdentifier.get(identifier);
161
+ if (existing && existing !== moduleSpecifier) {
162
+ state.importsByIdentifier.delete(identifier);
163
+ state.ambiguousIdentifiers.add(identifier);
164
+ return;
165
+ }
166
+ if (!existing) {
167
+ state.importsByIdentifier.set(identifier, moduleSpecifier);
168
+ }
169
+ }
170
+ function trackImportedIdentifierUsage(state, identifier, moduleSpecifier) {
171
+ if (!state.usedImports.has(moduleSpecifier)) {
172
+ state.usedImports.set(moduleSpecifier, new Set());
173
+ }
174
+ state.usedImports.get(moduleSpecifier).add(identifier);
175
+ }
176
+ function isBareModuleSpecifier(moduleSpecifier) {
177
+ return !!moduleSpecifier && !moduleSpecifier.startsWith('.') && !moduleSpecifier.startsWith('/');
178
+ }
179
+ function canUseComplexType(typeText, typeImportState) {
180
+ if (!typeText)
181
+ return false;
182
+ if (/[{};=]/.test(typeText) || /=>/.test(typeText))
183
+ return false;
184
+ if (!/^[A-Za-z0-9_$<>\[\]()|&,.?'"`\s:-]+$/.test(typeText))
185
+ return false;
186
+ for (const token of getIdentifierTokens(typeText)) {
187
+ if (isPrimitiveToken(token) || isKnownTypeIdentifier(token))
188
+ continue;
189
+ const root = token.split('.')[0];
190
+ if (typeImportState &&
191
+ !typeImportState.ambiguousIdentifiers.has(root) &&
192
+ typeImportState.importsByIdentifier.has(root)) {
193
+ trackImportedIdentifierUsage(typeImportState, root, typeImportState.importsByIdentifier.get(root));
194
+ continue;
195
+ }
196
+ return false;
197
+ }
198
+ return true;
199
+ }
200
+ function toSafeType(typeText, typeImportState) {
201
+ if (!typeText)
202
+ return 'unknown';
203
+ const normalized = normalizeWhitespace(typeText);
204
+ if (!normalized)
205
+ return 'unknown';
206
+ if (PRIMITIVE_UNION_REGEX.test(normalized))
207
+ return normalized;
208
+ if (normalized.endsWith('[]') && PRIMITIVE_UNION_REGEX.test(normalized.slice(0, -2).trim())) {
209
+ return normalized;
210
+ }
211
+ return canUseComplexType(normalized, typeImportState) ? normalized : 'unknown';
212
+ }
213
+ function extractDetailTypeFromDescription(description) {
214
+ var _a, _b;
215
+ return (_b = (_a = description === null || description === void 0 ? void 0 : description.match(/detail:\s*`([^`]+)`/)) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.trim();
216
+ }
217
+ /**
218
+ * Maps a CEM event type string to a TypeScript handler signature.
219
+ *
220
+ * - Known DOM event classes (Event, MouseEvent, FocusEvent, etc.) → `(event: T) => void`
221
+ * - `CustomEvent<Detail>` → `(event: CustomEvent<Detail>) => void`
222
+ * - Bare `CustomEvent` or unresolvable types → `(event: CustomEvent<unknown>) => void`
223
+ */
224
+ function toEventHandlerType(typeText, typeImportState, description) {
225
+ var _a;
226
+ if (typeText) {
227
+ const normalized = typeText.trim();
228
+ // Known DOM event classes map directly — not wrapped as CustomEvent<T> detail.
229
+ if (DOM_EVENT_CLASS_NAMES.has(normalized)) {
230
+ return `(event: ${normalized}) => void`;
231
+ }
232
+ if (normalized.startsWith('CustomEvent<')) {
233
+ const match = normalized.match(/^CustomEvent<(.+)>$/);
234
+ if (match) {
235
+ const detailType = toSafeType((_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim(), typeImportState);
236
+ return `(event: CustomEvent<${detailType}>) => void`;
237
+ }
238
+ }
239
+ // For any other resolvable non-DOM type, treat it as the CustomEvent detail payload.
240
+ if (normalized !== 'CustomEvent') {
241
+ const safeType = toSafeType(normalized, typeImportState);
242
+ if (safeType !== 'unknown') {
243
+ return `(event: CustomEvent<${safeType}>) => void`;
244
+ }
245
+ }
246
+ }
247
+ const detailFromDesc = extractDetailTypeFromDescription(description);
248
+ if (detailFromDesc) {
249
+ return `(event: CustomEvent<${toSafeType(detailFromDesc, typeImportState)}>) => void`;
250
+ }
251
+ return '(event: CustomEvent<unknown>) => void';
252
+ }
253
+ // ── Path helpers ─────────────────────────────────────────────────────────────
254
+ function getCEMManifestPath(cwd, packageJson) {
255
+ if (typeof packageJson.customElements === 'string' && packageJson.customElements.trim()) {
256
+ return (0, node_path_1.resolve)(cwd, packageJson.customElements);
257
+ }
258
+ return (0, node_path_1.resolve)(cwd, 'dist/custom-elements.json');
259
+ }
260
+ // CEM paths are relative to src/ (e.g. "src/entities/entities.ts").
261
+ // react.mjs/cjs live in dist/ while compiled JS lives in dist/esm/.
262
+ function cemModulePathToJsImport(modulePath) {
263
+ let p = modulePath.startsWith('src/') ? modulePath.slice(4) : modulePath;
264
+ if (p.endsWith('.tsx'))
265
+ p = `${p.slice(0, -4)}.js`;
266
+ else if (p.endsWith('.ts'))
267
+ p = `${p.slice(0, -3)}.js`;
268
+ return `./esm/${p}`;
269
+ }
270
+ function cemModulePathToDtsImport(modulePath) {
271
+ let p = modulePath.startsWith('src/') ? modulePath.slice(4) : modulePath;
272
+ const lastDot = p.lastIndexOf('.');
273
+ return `./${lastDot !== -1 ? p.slice(0, lastDot) : p}`;
274
+ }
275
+ // ── CEM traversal ─────────────────────────────────────────────────────────────
276
+ function collectCustomElements(manifest) {
277
+ var _a, _b, _c;
278
+ const elements = [];
279
+ for (const mod of (_a = manifest.modules) !== null && _a !== void 0 ? _a : []) {
280
+ const modulePath = (_b = mod.path) !== null && _b !== void 0 ? _b : '';
281
+ for (const decl of (_c = mod.declarations) !== null && _c !== void 0 ? _c : []) {
282
+ if (decl.customElement && decl.tagName) {
283
+ elements.push({ declaration: decl, modulePath });
284
+ }
285
+ }
286
+ }
287
+ return elements;
288
+ }
289
+ function mergeUniqueByKey(base, extra, getKey) {
290
+ const merged = [...base];
291
+ const knownKeys = new Set(base.map(getKey).filter((k) => !!k));
292
+ for (const item of extra) {
293
+ const key = getKey(item);
294
+ if (!key || !knownKeys.has(key)) {
295
+ merged.push(item);
296
+ if (key)
297
+ knownKeys.add(key);
298
+ }
299
+ }
300
+ return merged;
301
+ }
302
+ function mergeDeclarationMetadata(base, inherited) {
303
+ var _a, _b, _c, _d, _e, _f;
304
+ const attrKey = (a) => { var _a, _b; return normalizePropertyName((_b = (_a = a.fieldName) !== null && _a !== void 0 ? _a : a.name) !== null && _b !== void 0 ? _b : ''); };
305
+ const nameKey = (m) => { var _a; return normalizePropertyName((_a = m.name) !== null && _a !== void 0 ? _a : ''); };
306
+ const eventKey = (e) => { var _a; return normalizePropertyName((_a = e.name) !== null && _a !== void 0 ? _a : ''); };
307
+ return Object.assign(Object.assign({}, base), { attributes: mergeUniqueByKey((_a = base.attributes) !== null && _a !== void 0 ? _a : [], (_b = inherited.attributes) !== null && _b !== void 0 ? _b : [], attrKey), members: mergeUniqueByKey((_c = base.members) !== null && _c !== void 0 ? _c : [], (_d = inherited.members) !== null && _d !== void 0 ? _d : [], nameKey), events: mergeUniqueByKey((_e = base.events) !== null && _e !== void 0 ? _e : [], (_f = inherited.events) !== null && _f !== void 0 ? _f : [], eventKey) });
308
+ }
309
+ function createDeclarationLookup(manifest) {
310
+ var _a, _b;
311
+ const byTagAndName = new Map();
312
+ const byTag = new Map();
313
+ for (const mod of (_a = manifest.modules) !== null && _a !== void 0 ? _a : []) {
314
+ for (const decl of (_b = mod.declarations) !== null && _b !== void 0 ? _b : []) {
315
+ if (!decl.customElement || !decl.tagName)
316
+ continue;
317
+ if (decl.name)
318
+ byTagAndName.set(`${decl.tagName}::${decl.name}`, decl);
319
+ if (!byTag.has(decl.tagName))
320
+ byTag.set(decl.tagName, decl);
321
+ }
322
+ }
323
+ return { byTagAndName, byTag };
324
+ }
325
+ function mergeFastInheritanceFromManifest(cwd, entries) {
326
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
327
+ const fastManifestPath = (0, node_path_1.resolve)(cwd, 'ms-fast-components/custom-elements.json');
328
+ if (!(yield fileExists(fastManifestPath)))
329
+ return entries;
330
+ const fastManifest = JSON.parse(yield (0, promises_1.readFile)(fastManifestPath, 'utf8'));
331
+ const { byTagAndName, byTag } = createDeclarationLookup(fastManifest);
332
+ return entries.map((entry) => {
333
+ var _a, _b, _c, _d, _e, _f;
334
+ const { declaration } = entry;
335
+ if (!declaration.tagName || ((_a = declaration.superclass) === null || _a === void 0 ? void 0 : _a.package) !== '@microsoft/fast-components') {
336
+ return entry;
337
+ }
338
+ const inherited = (_f = (_c = byTagAndName.get(`${declaration.tagName}::${(_b = declaration.name) !== null && _b !== void 0 ? _b : ''}`)) !== null && _c !== void 0 ? _c : byTagAndName.get(`${declaration.tagName}::${(_e = (_d = declaration.superclass) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e : ''}`)) !== null && _f !== void 0 ? _f : byTag.get(declaration.tagName);
339
+ return inherited ? Object.assign(Object.assign({}, entry), { declaration: mergeDeclarationMetadata(declaration, inherited) }) : entry;
340
+ });
341
+ });
342
+ }
343
+ // ── Wrapper event helpers ─────────────────────────────────────────────────────
344
+ function buildWrapperEventEntries(declaration) {
345
+ var _a;
346
+ const result = [];
347
+ const seen = new Set();
348
+ for (const event of (_a = declaration.events) !== null && _a !== void 0 ? _a : []) {
349
+ if (!event.name)
350
+ continue;
351
+ const normalized = normalizePropertyName(event.name);
352
+ if (!normalized)
353
+ continue;
354
+ const handlerName = `on${toPascalCase(normalized)}`;
355
+ if (!handlerName || REACT_NATIVE_EVENT_HANDLER_NAMES.has(handlerName) || seen.has(handlerName))
356
+ continue;
357
+ seen.add(handlerName);
358
+ result.push({ handlerName, eventName: event.name });
359
+ }
360
+ return result;
361
+ }
362
+ function groupEntriesByPath(entries) {
363
+ const byPath = new Map();
364
+ for (const entry of entries) {
365
+ if (!byPath.has(entry.modulePath))
366
+ byPath.set(entry.modulePath, []);
367
+ byPath.get(entry.modulePath).push(entry);
368
+ }
369
+ return byPath;
370
+ }
371
+ function cloneTypeImportStateForWrapper(original) {
372
+ return {
373
+ importsByIdentifier: new Map(original.importsByIdentifier),
374
+ ambiguousIdentifiers: new Set(original.ambiguousIdentifiers),
375
+ usedImports: new Map(),
376
+ wildcardExportModules: new Set(original.wildcardExportModules),
377
+ };
378
+ }
379
+ // ── Code generation ───────────────────────────────────────────────────────────
380
+ function renderImportLines(usedImports) {
381
+ return [...usedImports.entries()]
382
+ .sort(([a], [b]) => a.localeCompare(b))
383
+ .map(([spec, ids]) => `import type { ${[...ids].sort().join(', ')} } from '${spec}';`);
384
+ }
385
+ function generateReactWrapperJs(entries, format) {
386
+ const valid = entries.filter((e) => e.declaration.name && e.modulePath);
387
+ if (!valid.length)
388
+ return '';
389
+ const esm = format === 'esm';
390
+ const lines = [
391
+ '/**',
392
+ ' * AUTO-GENERATED FILE - DO NOT EDIT.',
393
+ ' * Generated from custom-elements manifest.',
394
+ ' */',
395
+ '',
396
+ ];
397
+ if (!esm)
398
+ lines.push("'use strict';", '');
399
+ if (esm) {
400
+ lines.push("import { provideReactWrapper } from '@microsoft/fast-react-wrapper';", "import React from 'react';");
401
+ }
402
+ else {
403
+ lines.push("const { provideReactWrapper } = require('@microsoft/fast-react-wrapper');", "const React = require('react');");
404
+ }
405
+ for (const [modulePath, pathEntries] of [...groupEntriesByPath(valid).entries()].sort()) {
406
+ const sorted = [...pathEntries].sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
407
+ const jsPath = cemModulePathToJsImport(modulePath);
408
+ if (esm) {
409
+ lines.push(`import { ${sorted.map((e) => `${e.declaration.name} as ${e.declaration.name}WC`).join(', ')} } from '${jsPath}';`);
410
+ }
411
+ else {
412
+ lines.push(`const { ${sorted.map((e) => `${e.declaration.name}: ${e.declaration.name}WC`).join(', ')} } = require('${jsPath}');`);
413
+ }
414
+ }
415
+ lines.push('', 'const { wrap } = provideReactWrapper(React);', '');
416
+ for (const { declaration } of valid) {
417
+ const name = declaration.name;
418
+ const events = buildWrapperEventEntries(declaration);
419
+ const prefix = esm ? 'export const' : 'const';
420
+ if (!events.length) {
421
+ lines.push(`${prefix} ${name} = wrap(${name}WC);`);
422
+ }
423
+ else {
424
+ lines.push(`${prefix} ${name} = wrap(${name}WC, {`);
425
+ lines.push(' events: {');
426
+ for (const { handlerName, eventName } of events)
427
+ lines.push(` ${handlerName}: '${eventName}',`);
428
+ lines.push(' },');
429
+ lines.push('});');
430
+ }
431
+ lines.push('');
432
+ }
433
+ if (!esm) {
434
+ lines.push('module.exports = {');
435
+ for (const { declaration } of valid)
436
+ lines.push(` ${declaration.name},`);
437
+ lines.push('};', '');
438
+ }
439
+ return lines.join('\n');
440
+ }
441
+ function generateReactWrapperDts(entries, typeImportState) {
442
+ var _a;
443
+ const valid = entries.filter((e) => e.declaration.name && e.modulePath);
444
+ if (!valid.length)
445
+ return '';
446
+ const wrapperTypeState = cloneTypeImportStateForWrapper(typeImportState);
447
+ const classImports = [];
448
+ for (const [modulePath, pathEntries] of [...groupEntriesByPath(valid).entries()].sort()) {
449
+ const names = [...pathEntries]
450
+ .sort((a, b) => a.declaration.name.localeCompare(b.declaration.name))
451
+ .map((e) => `${e.declaration.name} as ${e.declaration.name}WC`)
452
+ .join(', ');
453
+ classImports.push(`import type { ${names} } from '${cemModulePathToDtsImport(modulePath)}';`);
454
+ }
455
+ const declarationLines = [];
456
+ for (const { declaration } of valid) {
457
+ const name = declaration.name;
458
+ // Build event lookup scoped to this element to avoid cross-element type pollution.
459
+ const eventsByName = new Map(((_a = declaration.events) !== null && _a !== void 0 ? _a : [])
460
+ .filter((e) => e.name)
461
+ .map((e) => [e.name, e]));
462
+ const eventLines = buildWrapperEventEntries(declaration).map(({ handlerName, eventName }) => {
463
+ var _a;
464
+ const event = eventsByName.get(eventName);
465
+ const handlerType = toEventHandlerType((_a = event === null || event === void 0 ? void 0 : event.type) === null || _a === void 0 ? void 0 : _a.text, wrapperTypeState, event === null || event === void 0 ? void 0 : event.description);
466
+ return ` ${handlerName}?: ${handlerType};`;
467
+ });
468
+ declarationLines.push(`export declare const ${name}: React.ForwardRefExoticComponent<`, ` React.PropsWithChildren<`, ` Omit<PublicOf<${name}WC>, 'children' | 'style'> &`, ` HTMLWCProps & {`, ...eventLines, ' }', ` > & React.RefAttributes<${name}WC>`, '>;', '');
469
+ }
470
+ return [
471
+ '/**',
472
+ ' * AUTO-GENERATED FILE - DO NOT EDIT.',
473
+ ' * Generated from custom-elements manifest.',
474
+ ' */',
475
+ '',
476
+ "import type React from 'react';",
477
+ ...classImports,
478
+ ...renderImportLines(wrapperTypeState.usedImports),
479
+ '',
480
+ HELPER_TYPES,
481
+ ...declarationLines,
482
+ 'export {};',
483
+ '',
484
+ ].join('\n');
485
+ }
486
+ // ── File system helpers ───────────────────────────────────────────────────────
487
+ function fileExists(path) {
488
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
489
+ try {
490
+ yield (0, promises_1.stat)(path);
491
+ return true;
492
+ }
493
+ catch (_a) {
494
+ return false;
495
+ }
496
+ });
497
+ }
498
+ function collectFilesRecursively(rootDirectory, isTargetFile) {
499
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
500
+ const files = [];
501
+ const stack = [rootDirectory];
502
+ while (stack.length) {
503
+ const dir = stack.pop();
504
+ const dirEntries = yield (0, promises_1.readdir)(dir, { withFileTypes: true });
505
+ for (const entry of dirEntries) {
506
+ const fullPath = (0, node_path_1.resolve)(dir, entry.name);
507
+ if (entry.isDirectory())
508
+ stack.push(fullPath);
509
+ else if (entry.isFile() && isTargetFile(entry.name))
510
+ files.push(fullPath);
511
+ }
512
+ }
513
+ return files;
514
+ });
515
+ }
516
+ function collectTypeMetadataFiles(distDirectory) {
517
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
518
+ const [apiJsonFiles, dtsFiles] = yield Promise.all([
519
+ collectFilesRecursively(distDirectory, (n) => n.endsWith('.api.json')),
520
+ collectFilesRecursively(distDirectory, (n) => n.endsWith('.d.ts')),
521
+ ]);
522
+ return { apiJsonFiles, dtsFiles };
523
+ });
524
+ }
525
+ // ── Type import state builders ────────────────────────────────────────────────
526
+ function addCanonicalReferenceFromValue(value, state) {
527
+ if (!value || typeof value !== 'object')
528
+ return;
529
+ if (Array.isArray(value)) {
530
+ for (const item of value)
531
+ addCanonicalReferenceFromValue(item, state);
532
+ return;
533
+ }
534
+ const record = value;
535
+ if (typeof record.canonicalReference === 'string') {
536
+ const match = record.canonicalReference.match(/^(@[^!]+)!([^:]+):/);
537
+ if (match) {
538
+ const [, moduleSpecifier, identifier] = match;
539
+ if (identifier && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) {
540
+ registerTypeImport(state, identifier, moduleSpecifier);
541
+ }
542
+ }
543
+ }
544
+ for (const nested of Object.values(record)) {
545
+ if (nested && typeof nested === 'object')
546
+ addCanonicalReferenceFromValue(nested, state);
547
+ }
548
+ }
549
+ function parseImportsFromDtsContent(content, state) {
550
+ var _a, _b, _c;
551
+ const blockRe = /(?:import|export)\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
552
+ for (const match of content.matchAll(blockRe)) {
553
+ const moduleSpecifier = (_a = match[2]) !== null && _a !== void 0 ? _a : '';
554
+ if (!isBareModuleSpecifier(moduleSpecifier))
555
+ continue;
556
+ for (const raw of ((_b = match[1]) !== null && _b !== void 0 ? _b : '').split(',')) {
557
+ const entry = raw.trim();
558
+ if (!entry)
559
+ continue;
560
+ const alias = entry.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)$/);
561
+ if (alias) {
562
+ registerTypeImport(state, alias[2], moduleSpecifier);
563
+ continue;
564
+ }
565
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(entry))
566
+ registerTypeImport(state, entry, moduleSpecifier);
567
+ }
568
+ }
569
+ const reExportRe = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
570
+ for (const match of content.matchAll(reExportRe)) {
571
+ const moduleSpecifier = (_c = match[1]) !== null && _c !== void 0 ? _c : '';
572
+ if (isBareModuleSpecifier(moduleSpecifier))
573
+ state.wildcardExportModules.add(moduleSpecifier);
574
+ }
575
+ }
576
+ function parseApiJsonFiles(filePaths, state) {
577
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
578
+ yield Promise.all(filePaths.map((filePath) => tslib_1.__awaiter(this, void 0, void 0, function* () {
579
+ const json = JSON.parse(yield (0, promises_1.readFile)(filePath, 'utf8'));
580
+ addCanonicalReferenceFromValue(json, state);
581
+ })));
582
+ });
583
+ }
584
+ function parseDtsFiles(filePaths, state) {
585
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
586
+ yield Promise.all(filePaths.map((filePath) => tslib_1.__awaiter(this, void 0, void 0, function* () {
587
+ parseImportsFromDtsContent(yield (0, promises_1.readFile)(filePath, 'utf8'), state);
588
+ })));
589
+ });
590
+ }
591
+ function mergeTypeImportStateInto(target, source) {
592
+ for (const id of source.ambiguousIdentifiers) {
593
+ target.importsByIdentifier.delete(id);
594
+ target.ambiguousIdentifiers.add(id);
595
+ }
596
+ for (const [identifier, spec] of source.importsByIdentifier) {
597
+ registerTypeImport(target, identifier, spec);
598
+ }
599
+ for (const mod of source.wildcardExportModules) {
600
+ target.wildcardExportModules.add(mod);
601
+ }
602
+ }
603
+ function findWorkspaceRoot(startDirectory) {
604
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
605
+ let dir = startDirectory;
606
+ while (true) {
607
+ const [hasPackages, hasPackageJson] = yield Promise.all([
608
+ fileExists((0, node_path_1.resolve)(dir, 'packages')),
609
+ fileExists((0, node_path_1.resolve)(dir, 'package.json')),
610
+ ]);
611
+ if (hasPackages && hasPackageJson)
612
+ return dir;
613
+ const parent = (0, node_path_1.dirname)(dir);
614
+ if (parent === dir)
615
+ return startDirectory;
616
+ dir = parent;
617
+ }
618
+ });
619
+ }
620
+ function getWorkspacePackageDirectoryByName(workspaceRoot, packageName) {
621
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
622
+ const packagesDir = (0, node_path_1.resolve)(workspaceRoot, 'packages');
623
+ if (!(yield fileExists(packagesDir)))
624
+ return undefined;
625
+ const packageJsonFiles = yield collectFilesRecursively(packagesDir, (n) => n === 'package.json');
626
+ for (const jsonPath of packageJsonFiles) {
627
+ const pkg = JSON.parse(yield (0, promises_1.readFile)(jsonPath, 'utf8'));
628
+ if (pkg.name === packageName)
629
+ return (0, node_path_1.dirname)(jsonPath);
630
+ }
631
+ return undefined;
632
+ });
633
+ }
634
+ function enrichFromWorkspaceWildcardExports(cwd, state) {
635
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
636
+ const workspaceRoot = yield findWorkspaceRoot(cwd);
637
+ // for...of over a Set processes items added inside the loop (spec-guaranteed behaviour).
638
+ const pending = new Set([...state.wildcardExportModules].filter((m) => m.startsWith('@genesislcap/')));
639
+ for (const moduleSpecifier of pending) {
640
+ const pkgDir = yield getWorkspacePackageDirectoryByName(workspaceRoot, moduleSpecifier);
641
+ if (!pkgDir)
642
+ continue;
643
+ const distDir = (0, node_path_1.resolve)(pkgDir, 'dist');
644
+ if (!(yield fileExists(distDir)))
645
+ continue;
646
+ const refState = createTypeImportState();
647
+ const { apiJsonFiles, dtsFiles } = yield collectTypeMetadataFiles(distDir);
648
+ yield parseApiJsonFiles(apiJsonFiles, refState);
649
+ yield parseDtsFiles(dtsFiles, refState);
650
+ mergeTypeImportStateInto(state, refState);
651
+ for (const mod of refState.wildcardExportModules) {
652
+ if (mod.startsWith('@genesislcap/'))
653
+ pending.add(mod);
654
+ }
655
+ }
656
+ });
657
+ }
658
+ function buildTypeImportState(cwd) {
659
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
660
+ const state = createTypeImportState();
661
+ const distDir = (0, node_path_1.resolve)(cwd, 'dist');
662
+ if (!(yield fileExists(distDir)))
663
+ return state;
664
+ const { apiJsonFiles, dtsFiles } = yield collectTypeMetadataFiles(distDir);
665
+ yield parseApiJsonFiles(apiJsonFiles, state);
666
+ yield parseDtsFiles(dtsFiles, state);
667
+ yield enrichFromWorkspaceWildcardExports(cwd, state);
668
+ return state;
669
+ });
670
+ }
671
+ // ── Entry point ───────────────────────────────────────────────────────────────
672
+ function generateReactWrappers(cwd) {
673
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
674
+ const packageJsonPath = (0, node_path_1.resolve)(cwd, 'package.json');
675
+ if (!(yield fileExists(packageJsonPath))) {
676
+ return { generated: false, reason: 'No package.json found.' };
677
+ }
678
+ const packageJson = JSON.parse(yield (0, promises_1.readFile)(packageJsonPath, 'utf8'));
679
+ const manifestPath = getCEMManifestPath(cwd, packageJson);
680
+ if (!(yield fileExists(manifestPath))) {
681
+ return { generated: false, reason: 'No custom elements manifest found.' };
682
+ }
683
+ const manifest = JSON.parse(yield (0, promises_1.readFile)(manifestPath, 'utf8'));
684
+ const rawEntries = collectCustomElements(manifest);
685
+ if (!rawEntries.length) {
686
+ return { generated: false, reason: 'No custom elements discovered in manifest.' };
687
+ }
688
+ const entries = yield mergeFastInheritanceFromManifest(cwd, rawEntries);
689
+ const hasEvents = entries.some((e) => { var _a; return ((_a = e.declaration.events) !== null && _a !== void 0 ? _a : []).length > 0; });
690
+ if (!hasEvents) {
691
+ return { generated: false, reason: 'No custom events found in any element.' };
692
+ }
693
+ const dtsRoot = (0, node_path_1.resolve)(cwd, 'dist/dts');
694
+ yield (0, promises_1.mkdir)(dtsRoot, { recursive: true });
695
+ const typeImportState = yield buildTypeImportState(cwd);
696
+ const reactDtsPath = (0, node_path_1.resolve)(dtsRoot, 'react.d.ts');
697
+ yield Promise.all([
698
+ (0, promises_1.writeFile)((0, node_path_1.resolve)(cwd, 'dist/react.mjs'), generateReactWrapperJs(entries, 'esm'), 'utf8'),
699
+ (0, promises_1.writeFile)((0, node_path_1.resolve)(cwd, 'dist/react.cjs'), generateReactWrapperJs(entries, 'cjs'), 'utf8'),
700
+ (0, promises_1.writeFile)(reactDtsPath, generateReactWrapperDts(entries, typeImportState), 'utf8'),
701
+ ]);
702
+ return { generated: true, path: reactDtsPath };
703
+ });
704
+ }