luxen-ui 0.19.0 → 0.19.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.19.0",
2
+ "version": "0.19.1",
3
3
  "elements": [
4
4
  {
5
5
  "name": "alert",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luxen-ui",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "Modern web components and CSS-first UI library built with Lit. Framework-agnostic, customizable prefix, design tokens.",
5
5
  "keywords": [
6
6
  "custom-elements",
package/vite-plugin.ts CHANGED
@@ -6,29 +6,45 @@ import postcssPrefix from './postcss-plugin-prefix.js';
6
6
 
7
7
  /**
8
8
  * Map of element base name → class name as exported by `luxen-ui/<name>/element`.
9
- * Keep in sync with `src/html/registry.ts` `ElementBaseName`.
10
9
  *
11
- * Still missing (an element absent here silently emits no typing, so `emitTypes`
12
- * consumers get an untyped `Element` for it): alert-dialog, button-group,
13
- * combobox, dropdown-label, form-field, input-group, prose-editor,
14
- * segmented-control, select, tag.
10
+ * Every custom element the library defines must appear here: an element absent
11
+ * from this map emits no typing at all (`emitTypes` consumers get an untyped
12
+ * `Element`), and an entry naming a class that isn't exported at
13
+ * `<name>/element` emits an import with no file behind it. Neither is
14
+ * observable at runtime, so `scripts/check-metadata.mjs` (check 6) validates
15
+ * both directions against `dist/custom-elements.json` on every build — that is
16
+ * why this map is exported. The same check ties each element back to
17
+ * `ElementBaseName` in `src/html/registry.ts`, so all three stay in step.
18
+ *
19
+ * `toast-item` is deliberately absent: it is registered from `toast/index.ts`
20
+ * and has no `dist/elements/toast-item/` module to import a type from.
15
21
  */
16
- const ELEMENT_CLASSES: Record<string, string> = {
22
+ export const ELEMENT_CLASSES: Record<string, string> = {
17
23
  alert: 'Alert',
24
+ 'alert-dialog': 'AlertDialog',
18
25
  avatar: 'Avatar',
19
26
  badge: 'Badge',
27
+ 'button-group': 'ButtonGroup',
20
28
  carousel: 'Carousel',
21
29
  'carousel-item': 'CarouselItem',
30
+ 'color-scheme-icon': 'ColorSchemeIcon',
31
+ combobox: 'Combobox',
22
32
  dialog: 'Dialog',
23
33
  divider: 'Divider',
24
34
  drawer: 'Drawer',
25
35
  dropdown: 'Dropdown',
26
36
  'dropdown-item': 'DropdownItem',
37
+ 'dropdown-label': 'DropdownLabel',
38
+ 'form-field': 'FormField',
27
39
  icon: 'Icon',
40
+ 'input-group': 'InputGroup',
28
41
  'input-otp': 'InputOtp',
29
42
  'input-stepper': 'InputStepper',
30
43
  popover: 'Popover',
44
+ 'prose-editor': 'ProseEditor',
31
45
  rating: 'Rating',
46
+ 'segmented-control': 'SegmentedControl',
47
+ select: 'Select',
32
48
  skeleton: 'Skeleton',
33
49
  slider: 'Slider',
34
50
  spinner: 'Spinner',
@@ -37,8 +53,8 @@ const ELEMENT_CLASSES: Record<string, string> = {
37
53
  'stories-viewer': 'LuxenStoriesViewer',
38
54
  story: 'LuxenStory',
39
55
  tabs: 'Tabs',
56
+ tag: 'Tag',
40
57
  toast: 'Toast',
41
- 'toast-item': 'ToastItem',
42
58
  tooltip: 'Tooltip',
43
59
  tree: 'Tree',
44
60
  'tree-item': 'TreeItem',
@@ -241,12 +257,19 @@ async function loadLuxenConfig(): Promise<Partial<LuxenOptions> | null> {
241
257
  function syncTypesFile(prefix: string, cfg: EmitTypesOptions): void {
242
258
  const target = resolve(process.cwd(), cfg.path);
243
259
  const names = cfg.elements ?? Object.keys(ELEMENT_CLASSES);
244
- const unknown = names.filter((n) => !(n in ELEMENT_CLASSES));
260
+ // Own-property lookup, not `in`: a hand-written `elements` subset is consumer
261
+ // input, and `'toString' in ELEMENT_CLASSES` is true — the prototype chain
262
+ // would smuggle `Object`'s own members into the emitted file. (`Object.hasOwn`
263
+ // would read better but is ES2022; this file compiles against `lib: ES2020`.)
264
+ const isElement = (n: string): boolean =>
265
+ Object.prototype.hasOwnProperty.call(ELEMENT_CLASSES, n);
266
+
267
+ const unknown = names.filter((n) => !isElement(n));
245
268
  if (unknown.length > 0) {
246
269
  console.warn(`[luxen] emitTypes: unknown element(s) ignored — ${unknown.join(', ')}`);
247
270
  }
248
271
 
249
- const known = names.filter((n) => n in ELEMENT_CLASSES);
272
+ const known = names.filter(isElement);
250
273
  const pkg = cfg.packageName || 'luxen-ui';
251
274
  const content =
252
275
  cfg.target === 'vue'
@@ -323,16 +346,23 @@ import type { DefineComponent } from 'vue';
323
346
  import type { LuxenElement } from '${pkg}/luxen-element';
324
347
  ${imports}
325
348
 
326
- // Keep only each element's OWN data props (subtract everything inherited from
327
- // LuxenElement / Lit / HTMLElement), drop methods, and allow any \`on*\`
328
- // listener. Autocomplete stays on the real props while unknown attributes and
329
- // bad values still error.
349
+ // Keep only each element's OWN settable data props: subtract everything
350
+ // inherited from LuxenElement / Lit / HTMLElement, drop methods, and drop
351
+ // read-only members — get-only accessors (\`validity\`, \`willValidate\`) and
352
+ // \`readonly\` fields (\`_internals\`) are element state a template cannot set,
353
+ // and several elements inherit a stack of them from the form-associated base.
354
+ // Any \`on*\` listener stays allowed. Autocomplete lands on the real props while
355
+ // unknown attributes and bad values still error.
356
+ type Writable<T, K extends keyof T> = (<G>() => G extends { [P in K]: T[K] } ? 1 : 2) extends
357
+ <G>() => G extends { -readonly [P in K]: T[K] } ? 1 : 2
358
+ ? K
359
+ : never;
330
360
  type OwnKeys<T> = {
331
361
  [K in keyof T]-?: K extends keyof LuxenElement
332
362
  ? never
333
363
  : T[K] extends (...args: never[]) => unknown
334
364
  ? never
335
- : K;
365
+ : Writable<T, K>;
336
366
  }[keyof T];
337
367
  type ElementProps<T> = Partial<Pick<T, OwnKeys<T>>> &
338
368
  Record<\`on\${string}\`, ((event: Event) => void) | undefined>;