@ui5/webcomponents-tools 0.0.0-47cc17a26 → 0.0.0-49bade48d

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 (37) hide show
  1. package/CHANGELOG.md +327 -1
  2. package/assets-meta.js +6 -4
  3. package/bin/dev.js +9 -4
  4. package/bin/ui5nps.js +44 -8
  5. package/components-package/eslint.js +1 -1
  6. package/components-package/nps.js +23 -18
  7. package/icons-collection/nps.js +1 -1
  8. package/lib/amd-to-es6/index.js +3 -1
  9. package/lib/cem/cem.js +4 -0
  10. package/lib/cem/custom-elements-manifest.config.mjs +88 -4
  11. package/lib/cem/merge.mjs +220 -0
  12. package/lib/cem/schema-internal.json +41 -1
  13. package/lib/cem/schema.json +41 -1
  14. package/lib/cem/types-internal.d.ts +32 -2
  15. package/lib/cem/types.d.ts +32 -2
  16. package/lib/cem/utils.mjs +13 -3
  17. package/lib/cem/validate.js +7 -2
  18. package/lib/chokidar/chokidar.js +28 -0
  19. package/lib/copy-and-watch/index.js +5 -0
  20. package/lib/copy-list/index.js +3 -1
  21. package/lib/create-icons/index.js +18 -13
  22. package/lib/create-illustrations/index.js +46 -4
  23. package/lib/css-processors/css-processor-components.mjs +14 -3
  24. package/lib/css-processors/css-processor-themes.mjs +148 -24
  25. package/lib/css-processors/merge-light-dark.mjs +131 -0
  26. package/lib/css-processors/postcss-plugin.mjs +153 -0
  27. package/lib/css-processors/scope-variables.mjs +26 -1
  28. package/lib/css-processors/shared.mjs +7 -6
  29. package/lib/eslint/eslint.js +44 -0
  30. package/lib/generate-js-imports/illustrations.js +3 -1
  31. package/lib/generate-json-imports/i18n.js +3 -1
  32. package/lib/generate-json-imports/themes.js +6 -2
  33. package/lib/i18n/defaults.js +3 -1
  34. package/lib/i18n/toJSON.js +28 -4
  35. package/lib/test-runner/test-runner.js +56 -48
  36. package/lib/vite-bundler/vite-bundler.mjs +35 -0
  37. package/package.json +2 -4
@@ -17,6 +17,7 @@ import {
17
17
  getTypeRefs,
18
18
  normalizeDescription,
19
19
  formatArrays,
20
+ formatSlotTypes,
20
21
  isClass,
21
22
  normalizeTagType,
22
23
  logDocumentationError,
@@ -27,7 +28,44 @@ import { generateCustomData } from "cem-plugin-vs-code-custom-data-generator";
27
28
  import { customElementJetBrainsPlugin } from "custom-element-jet-brains-integration";
28
29
 
29
30
  const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
31
+ let aliasMap = {};
32
+
30
33
  const devMode = process.env.UI5_CEM_MODE === "dev";
34
+ const isVerbose = process.env.UI5_VERBOSE === "true";
35
+
36
+ /**
37
+ * Wraps a CEM plugin to suppress console output in quiet mode.
38
+ * @param {Object} plugin - The plugin to wrap
39
+ * @returns {Object} - Wrapped plugin with silent console during packageLinkPhase
40
+ */
41
+ const wrapPluginForQuietMode = (plugin) => {
42
+ if (isVerbose) return plugin;
43
+
44
+ const originalPackageLinkPhase = plugin.packageLinkPhase;
45
+ if (!originalPackageLinkPhase) return plugin;
46
+
47
+ return {
48
+ ...plugin,
49
+ packageLinkPhase(context) {
50
+ const originalLog = console.log;
51
+ console.log = () => { };
52
+ try {
53
+ return originalPackageLinkPhase.call(plugin, context);
54
+ } finally {
55
+ console.log = originalLog;
56
+ }
57
+ }
58
+ };
59
+ };
60
+
61
+ try {
62
+ aliasMap = JSON.parse(fs.readFileSync("./.ui5-cem-aliases.json"));
63
+ } catch (e) {
64
+ if (devMode && isVerbose) {
65
+ console.warn("No .ui5-cem-aliases.json file found. Continuing without aliases.");
66
+ }
67
+ }
68
+
31
69
 
32
70
  const extractClassNodeJSDoc = node => {
33
71
  const fileContent = node.getFullText();
@@ -97,6 +135,13 @@ function processClass(ts, classNode, moduleDoc) {
97
135
  if (currClass.superclass?.name === "UI5Element") {
98
136
  currClass.customElement = true;
99
137
  }
138
+ } else if (currClass.customElement && classNode?.heritageClauses) {
139
+ // Find the extends clause (not implements)
140
+ const extendsClause = classNode.heritageClauses.find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
141
+ if (extendsClause?.types?.[0]?.expression?.text) {
142
+ const extendedClass = extendsClause.types[0].expression.text;
143
+ logDocumentationError(moduleDoc.path, `Class extends ${extendedClass} but @extends tag is missing in JSDoc`);
144
+ }
100
145
  }
101
146
 
102
147
  if (!currClass._ui5implements.length) delete currClass._ui5implements;
@@ -158,6 +203,7 @@ function processClass(ts, classNode, moduleDoc) {
158
203
 
159
204
  if (member.type?.text) {
160
205
  member.type.text = formatArrays(member.type.text);
206
+ member.type.text = formatSlotTypes(member.type.text);
161
207
  }
162
208
 
163
209
  if (member.type && typeRefs.length) {
@@ -378,10 +424,10 @@ const processPublicAPI = object => {
378
424
  return true;
379
425
  }
380
426
  for (const key of keys) {
381
- if ((key === "privacy" && object[key] !== "public") || (key === "_ui5privacy" && object[key] !== "public")) {
427
+ if (((key === "privacy" && object[key] !== "public") || (key === "_ui5privacy" && object[key] !== "public")) && !object.customElement) {
382
428
  return true;
383
429
  } else if (typeof object[key] === "object") {
384
- if (key === "cssParts" || key === "attributes" || key === "_ui5implements") {
430
+ if (key === "cssParts" || key === "cssStates" || key === "attributes" || key === "_ui5implements") {
385
431
  continue;
386
432
  }
387
433
 
@@ -486,6 +532,12 @@ export default {
486
532
  }
487
533
  }
488
534
 
535
+ moduleDoc.declarations.forEach(declaration => {
536
+ if (declaration.superclass?.name && aliasMap[declaration.superclass.name]) {
537
+ declaration.superclass.name = aliasMap[declaration.superclass.name];
538
+ }
539
+ })
540
+
489
541
  const typeReferences = new Set();
490
542
  const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
491
543
 
@@ -524,7 +576,39 @@ export default {
524
576
  }
525
577
  }
526
578
  },
527
- generateCustomData({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false }),
528
- customElementJetBrainsPlugin({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false })
579
+ {
580
+ name: 'alphabetical-sort-plugin',
581
+ packageLinkPhase({ customElementsManifest }) {
582
+ const sortByName = (arr) => {
583
+ if (Array.isArray(arr)) {
584
+ arr.sort((a, b) => {
585
+ if (a?.name && b?.name) {
586
+ return a.name.localeCompare(b.name);
587
+ }
588
+ return 0;
589
+ });
590
+ }
591
+ };
592
+
593
+ const sortArraysInObject = (obj) => {
594
+ if (!obj || typeof obj !== 'object') return;
595
+
596
+ for (const key in obj) {
597
+ if (Array.isArray(obj[key])) {
598
+ sortByName(obj[key]);
599
+ obj[key].forEach(item => sortArraysInObject(item));
600
+ } else if (typeof obj[key] === 'object') {
601
+ sortArraysInObject(obj[key]);
602
+ }
603
+ }
604
+ };
605
+
606
+ customElementsManifest.modules?.forEach(moduleDoc => {
607
+ sortArraysInObject(moduleDoc);
608
+ });
609
+ }
610
+ },
611
+ wrapPluginForQuietMode(generateCustomData({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false })),
612
+ wrapPluginForQuietMode(customElementJetBrainsPlugin({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false }))
529
613
  ],
530
614
  };
@@ -0,0 +1,220 @@
1
+ import { pathToFileURL } from "url";
2
+ import path from "path";
3
+ import { createRequire } from 'module';
4
+ import { readFile, writeFile } from "fs/promises";
5
+
6
+ const require = createRequire(import.meta.url);
7
+
8
+ const UI5_BASE_CLASS = "UI5Element";
9
+
10
+ const main = async (argv) => {
11
+ let customElementsPath = null;
12
+ const CACHED_CEMS = new Map();
13
+ const DECLARATION_PACKAGE = new WeakMap();
14
+ const DECLARATION_MODULE = new WeakMap();
15
+
16
+ function removeInheritedFrom(obj) {
17
+ if (obj === null || typeof obj !== 'object') {
18
+ return obj;
19
+ }
20
+
21
+ if (Array.isArray(obj)) {
22
+ return obj.map(item => removeInheritedFrom(item));
23
+ }
24
+
25
+ const result = {};
26
+ for (const [key, value] of Object.entries(obj)) {
27
+ if (key === 'inheritedFrom') {
28
+ continue;
29
+ }
30
+ result[key] = removeInheritedFrom(value);
31
+ }
32
+ return result;
33
+ }
34
+
35
+ async function readPackageJson(filePath) {
36
+ try {
37
+ return JSON.parse(await readFile(filePath, "utf-8"));
38
+ } catch (error) {
39
+ throw new Error(`Failed to read package.json at ${filePath}: ${error.message}`);
40
+ }
41
+ }
42
+
43
+ async function loadPackageJson(depName) {
44
+ try {
45
+ // First try the standard require method (works when exports includes package.json)
46
+ const pkg = require(`${depName}/package.json`);
47
+ const pkgPath = require.resolve(`${depName}/package.json`);
48
+ return { path: path.dirname(pkgPath), pkg };
49
+ } catch (e) {
50
+ // If that fails, resolve the package path and read package.json directly
51
+ try {
52
+ const packagePath = require.resolve(depName);
53
+ let currentDir = path.dirname(packagePath);
54
+
55
+ // Navigate up to find package.json (the resolved path might be deep in dist/ or similar)
56
+ while (currentDir !== path.parse(currentDir).root) {
57
+ try {
58
+ const pkgPath = path.join(currentDir, 'package.json');
59
+ const content = await readFile(pkgPath, 'utf-8');
60
+ const pkg = JSON.parse(content);
61
+
62
+ // Verify this is the correct package.json by checking the name
63
+ if (pkg.name === depName) {
64
+ return { path: currentDir, pkg };
65
+ }
66
+ } catch {
67
+ // Continue searching up the directory tree
68
+ }
69
+ currentDir = path.dirname(currentDir);
70
+ }
71
+ } catch (resolveError) {
72
+ // console.warn(`Could not resolve ${depName}:`, resolveError.message);
73
+ }
74
+ return null;
75
+ }
76
+ }
77
+
78
+ async function collectThirdPartyCem() {
79
+ const packageJSONPath = path.resolve(process.cwd(), "package.json");
80
+ const packageJSON = await readPackageJson(packageJSONPath);
81
+
82
+ const dependencyKeys = Object.keys(packageJSON).filter(key => key.toLowerCase().includes("dependencies"));
83
+ const dependencies = dependencyKeys.flatMap(key => Object.keys(packageJSON[key]));
84
+
85
+ const thirdPartCEM = (await Promise.all(dependencies.map(async dep => {
86
+ const result = await loadPackageJson(dep);
87
+ if (!result?.pkg?.customElements) return null;
88
+
89
+ return {
90
+ path: result.path,
91
+ name: dep,
92
+ cem: result.pkg.customElements
93
+ };
94
+ }))).filter(Boolean);
95
+
96
+ await Promise.all(thirdPartCEM.map(async dep => {
97
+ const cemPath = path.resolve(dep.path, dep.cem);
98
+ try {
99
+ const cemContent = JSON.parse(await readFile(cemPath, "utf-8"));
100
+ CACHED_CEMS.set(dep.name, cemContent);
101
+ } catch (error) {
102
+ console.warn(`Failed to read CEM for ${dep.name} from ${cemPath}: ${error.message}`);
103
+ }
104
+ }));
105
+ }
106
+
107
+ async function readCurrentCEM() {
108
+ const packageJSONPath = path.resolve(process.cwd(), "package.json");
109
+ const packageJSON = await readPackageJson(packageJSONPath);
110
+
111
+ if (!packageJSON?.customElements) {
112
+ return null;
113
+ }
114
+
115
+ customElementsPath = packageJSON.customElements;
116
+ const cemPath = path.resolve(process.cwd(), customElementsPath);
117
+
118
+ try {
119
+ const cemContent = JSON.parse(await readFile(cemPath, "utf-8"));
120
+ CACHED_CEMS.set(packageJSON.name, cemContent);
121
+ return cemContent;
122
+ } catch (error) {
123
+ throw new Error(`Failed to read CEM from ${cemPath}: ${error.message}`);
124
+ }
125
+ }
126
+
127
+ async function resolveReference(ref) {
128
+ const pkg = CACHED_CEMS.get(ref.package);
129
+
130
+ if (!pkg) {
131
+ return null;
132
+ }
133
+
134
+ const mod = (pkg.modules || []).find(m => m.path === ref.module);
135
+
136
+ if (!mod) {
137
+ return null;
138
+ }
139
+
140
+ const declaration = (mod.declarations || []).find(d => d.name === ref.name);
141
+
142
+ if (!declaration) {
143
+ return null;
144
+ }
145
+
146
+ DECLARATION_PACKAGE.set(declaration, ref.package);
147
+ DECLARATION_MODULE.set(declaration, ref.module);
148
+
149
+ return resolveDeclaration(declaration);
150
+ }
151
+
152
+ async function resolveDeclaration(declaration) {
153
+ if (!declaration.superclass || declaration.superclass.name === UI5_BASE_CLASS) {
154
+ return [declaration];
155
+ }
156
+
157
+ const superclassDeclarations = await resolveReference(declaration.superclass);
158
+ return [declaration, superclassDeclarations].flat().filter(Boolean);
159
+ }
160
+
161
+ const merge = async () => {
162
+ const currentCEM = await readCurrentCEM();
163
+ if (!currentCEM) {
164
+ throw new Error("No custom elements manifest found in current project");
165
+ }
166
+
167
+ await collectThirdPartyCem();
168
+
169
+ const modules = currentCEM.modules || [];
170
+
171
+ for (const mod of modules) {
172
+ const declarations = (mod.declarations || []).filter(d => d.kind === "class");
173
+
174
+ for (const declaration of declarations) {
175
+ const declarationHierarchy = await resolveDeclaration(declaration);
176
+ const allKeys = declarationHierarchy.flatMap(dec => Object.keys(dec));
177
+ const uniqueKeys = [...new Set(allKeys)];
178
+ const arrayKeys = uniqueKeys
179
+ .filter(key => !key.startsWith("_ui5"))
180
+ .filter(key => declarationHierarchy.some(dec => Array.isArray(dec[key])));
181
+
182
+ for (const key of arrayKeys) {
183
+ const allItems = declarationHierarchy.flatMap(dec => dec[key] || []);
184
+
185
+ // Remove duplicates based on name property
186
+ const seen = new Set();
187
+ declaration[key] = allItems.filter(item => {
188
+ if (!item.name) return true;
189
+ if (seen.has(item.name)) return false;
190
+ seen.add(item.name);
191
+ return true;
192
+ });
193
+ }
194
+ }
195
+ }
196
+
197
+ const cleanedCEM = removeInheritedFrom(currentCEM);
198
+ const outputPath = path.resolve(process.cwd(), customElementsPath);
199
+
200
+ try {
201
+ await writeFile(outputPath, JSON.stringify(cleanedCEM, null, 2), "utf-8");
202
+ console.log(`Successfully merged CEM to ${outputPath}`);
203
+ } catch (error) {
204
+ throw new Error(`Failed to write merged CEM to ${outputPath}: ${error.message}`);
205
+ }
206
+ };
207
+
208
+ await merge();
209
+ }
210
+
211
+ const filePath = process.argv[1];
212
+ const fileUrl = pathToFileURL(filePath).href;
213
+
214
+ if (import.meta.url === fileUrl) {
215
+ main(process.argv)
216
+ }
217
+
218
+ export default {
219
+ _ui5mainFn: main
220
+ }
@@ -1,6 +1,34 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "definitions": {
4
+ "CssCustomState": {
5
+ "description": "The description of a CSS Custom State\nhttps://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet",
6
+ "properties": {
7
+ "deprecated": {
8
+ "description": "Whether the CSS custom state is deprecated.\nIf the value is a string, it's the reason for the deprecation.",
9
+ "type": [
10
+ "string",
11
+ "boolean"
12
+ ]
13
+ },
14
+ "description": {
15
+ "description": "A markdown description.",
16
+ "type": "string"
17
+ },
18
+ "name": {
19
+ "description": "The name of the state. Note: Unlike CSS custom properties, custom states\ndo not have a leading `--`.",
20
+ "type": "string"
21
+ },
22
+ "summary": {
23
+ "description": "A markdown summary suitable for display in a listing.",
24
+ "type": "string"
25
+ }
26
+ },
27
+ "required": [
28
+ "name"
29
+ ],
30
+ "type": "object"
31
+ },
4
32
  "Attribute": {
5
33
  "additionalProperties": false,
6
34
  "properties": {
@@ -521,6 +549,12 @@
521
549
  },
522
550
  "type": "array"
523
551
  },
552
+ "cssStates": {
553
+ "items": {
554
+ "$ref": "#/definitions/CssCustomState"
555
+ },
556
+ "type": "array"
557
+ },
524
558
  "customElement": {
525
559
  "description": "Distinguishes a regular JavaScript class from a\ncustom element class",
526
560
  "enum": [
@@ -676,6 +710,12 @@
676
710
  },
677
711
  "type": "array"
678
712
  },
713
+ "cssStates": {
714
+ "items": {
715
+ "$ref": "#/definitions/CssCustomState"
716
+ },
717
+ "type": "array"
718
+ },
679
719
  "customElement": {
680
720
  "description": "Distinguishes a regular JavaScript class from a\ncustom element class",
681
721
  "enum": [
@@ -1039,7 +1079,7 @@
1039
1079
  "type": "string"
1040
1080
  },
1041
1081
  "path": {
1042
- "description": "Path to the javascript file needed to be imported. \n(not the path for example to a typescript file.)",
1082
+ "description": "Path to the javascript file needed to be imported.\n(not the path for example to a typescript file.)",
1043
1083
  "type": "string"
1044
1084
  },
1045
1085
  "summary": {
@@ -258,6 +258,34 @@
258
258
  ],
259
259
  "type": "object"
260
260
  },
261
+ "CssCustomState": {
262
+ "description": "The description of a CSS Custom State\nhttps://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet",
263
+ "properties": {
264
+ "deprecated": {
265
+ "description": "Whether the CSS custom state is deprecated.\nIf the value is a string, it's the reason for the deprecation.",
266
+ "type": [
267
+ "string",
268
+ "boolean"
269
+ ]
270
+ },
271
+ "description": {
272
+ "description": "A markdown description.",
273
+ "type": "string"
274
+ },
275
+ "name": {
276
+ "description": "The name of the state. Note: Unlike CSS custom properties, custom states\ndo not have a leading `--`.",
277
+ "type": "string"
278
+ },
279
+ "summary": {
280
+ "description": "A markdown summary suitable for display in a listing.",
281
+ "type": "string"
282
+ }
283
+ },
284
+ "required": [
285
+ "name"
286
+ ],
287
+ "type": "object"
288
+ },
261
289
  "CssPart": {
262
290
  "description": "The description of a CSS Part",
263
291
  "properties": {
@@ -307,6 +335,12 @@
307
335
  },
308
336
  "type": "array"
309
337
  },
338
+ "cssStates": {
339
+ "items": {
340
+ "$ref": "#/definitions/CssCustomState"
341
+ },
342
+ "type": "array"
343
+ },
310
344
  "customElement": {
311
345
  "description": "Distinguishes a regular JavaScript class from a\ncustom element class",
312
346
  "enum": [
@@ -451,6 +485,12 @@
451
485
  },
452
486
  "type": "array"
453
487
  },
488
+ "cssStates": {
489
+ "items": {
490
+ "$ref": "#/definitions/CssCustomState"
491
+ },
492
+ "type": "array"
493
+ },
454
494
  "customElement": {
455
495
  "description": "Distinguishes a regular JavaScript class from a\ncustom element class",
456
496
  "enum": [
@@ -765,7 +805,7 @@
765
805
  "type": "string"
766
806
  },
767
807
  "path": {
768
- "description": "Path to the javascript file needed to be imported. \n(not the path for example to a typescript file.)",
808
+ "description": "Path to the javascript file needed to be imported.\n(not the path for example to a typescript file.)",
769
809
  "type": "string"
770
810
  },
771
811
  "summary": {
@@ -56,7 +56,7 @@ export interface JavaScriptModule {
56
56
  kind: 'javascript-module';
57
57
 
58
58
  /**
59
- * Path to the javascript file needed to be imported.
59
+ * Path to the javascript file needed to be imported.
60
60
  * (not the path for example to a typescript file.)
61
61
  */
62
62
  path: string;
@@ -258,6 +258,8 @@ export interface CustomElement extends ClassLike {
258
258
 
259
259
  cssProperties?: CssCustomProperty[];
260
260
 
261
+ cssStates?: CssCustomState[];
262
+
261
263
  demos?: Demo[];
262
264
 
263
265
  /**
@@ -416,6 +418,34 @@ export interface CssPart {
416
418
  deprecated?: boolean | string;
417
419
  }
418
420
 
421
+ /**
422
+ * The description of a CSS Custom State
423
+ * https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet
424
+ */
425
+ export interface CssCustomState {
426
+ /**
427
+ * The name of the state. Note: Unlike CSS custom properties, custom states
428
+ * do not have a leading `--`.
429
+ */
430
+ name: string;
431
+
432
+ /**
433
+ * A markdown summary suitable for display in a listing.
434
+ */
435
+ summary?: string;
436
+
437
+ /**
438
+ * A markdown description.
439
+ */
440
+ description?: string;
441
+
442
+ /**
443
+ * Whether the CSS custom state is deprecated.
444
+ * If the value is a string, it's the reason for the deprecation.
445
+ */
446
+ deprecated?: boolean | string;
447
+ }
448
+
419
449
  export interface CssCustomProperty {
420
450
  /**
421
451
  * The name of the property, including leading `--`.
@@ -632,7 +662,7 @@ export interface ClassField extends PropertyLike {
632
662
  export interface CustomElementField extends ClassField {
633
663
  /**
634
664
  * The corresponding attribute name if there is one.
635
- *
665
+ *
636
666
  * If this property is defined, the attribute must be listed in the classes'
637
667
  * `attributes` array.
638
668
  */
@@ -56,7 +56,7 @@ export interface JavaScriptModule {
56
56
  kind: 'javascript-module';
57
57
 
58
58
  /**
59
- * Path to the javascript file needed to be imported.
59
+ * Path to the javascript file needed to be imported.
60
60
  * (not the path for example to a typescript file.)
61
61
  */
62
62
  path: string;
@@ -250,6 +250,8 @@ export interface CustomElement extends ClassLike {
250
250
 
251
251
  cssProperties?: CssCustomProperty[];
252
252
 
253
+ cssStates?: CssCustomState[];
254
+
253
255
  demos?: Demo[];
254
256
 
255
257
  /**
@@ -372,6 +374,34 @@ export interface CssPart {
372
374
  deprecated?: boolean | string;
373
375
  }
374
376
 
377
+ /**
378
+ * The description of a CSS Custom State
379
+ * https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet
380
+ */
381
+ export interface CssCustomState {
382
+ /**
383
+ * The name of the state. Note: Unlike CSS custom properties, custom states
384
+ * do not have a leading `--`.
385
+ */
386
+ name: string;
387
+
388
+ /**
389
+ * A markdown summary suitable for display in a listing.
390
+ */
391
+ summary?: string;
392
+
393
+ /**
394
+ * A markdown description.
395
+ */
396
+ description?: string;
397
+
398
+ /**
399
+ * Whether the CSS custom state is deprecated.
400
+ * If the value is a string, it's the reason for the deprecation.
401
+ */
402
+ deprecated?: boolean | string;
403
+ }
404
+
375
405
  export interface CssCustomProperty {
376
406
  /**
377
407
  * The name of the property, including leading `--`.
@@ -573,7 +603,7 @@ export interface ClassField extends PropertyLike {
573
603
  export interface CustomElementField extends ClassField {
574
604
  /**
575
605
  * The corresponding attribute name if there is one.
576
- *
606
+ *
577
607
  * If this property is defined, the attribute must be listed in the classes'
578
608
  * `attributes` array.
579
609
  */
package/lib/cem/utils.mjs CHANGED
@@ -240,7 +240,7 @@ const allowedTags = {
240
240
  event: [...commonTags, "param", "native", "allowPreventDefault"],
241
241
  eventParam: [...commonTags],
242
242
  method: [...commonTags, "param", "returns", "override"],
243
- class: [...commonTags, "constructor", "class", "abstract", "experimental", "implements", "extends", "slot", "csspart"],
243
+ class: [...commonTags, "constructor", "class", "abstract", "experimental", "implements", "extends", "slot", "csspart", "cssstate", "cssState"],
244
244
  enum: [...commonTags, "experimental",],
245
245
  enumMember: [...commonTags, "experimental",],
246
246
  interface: [...commonTags, "experimental",],
@@ -330,6 +330,9 @@ const validateJSDocTag = (tag) => {
330
330
  return tag.type && tag.name && tag.description;
331
331
  case "csspart":
332
332
  return !tag.type && tag.name && tag.description;
333
+ case "cssState":
334
+ case "cssstate":
335
+ return !tag.type && tag.name && tag.description;
333
336
  case "since":
334
337
  return !tag.type && tag.name;
335
338
  case "returns":
@@ -360,7 +363,8 @@ const validateJSDocComment = (fieldType, jsdocComment, node, moduleDoc) => {
360
363
  }
361
364
 
362
365
  if (!isValid) {
363
- logDocumentationError(moduleDoc.path, `Incorrect use of @${tag.tag}. Ensure it is part of ${fieldType} JSDoc tags.`)
366
+ const nodeName = node ? ` in '${node}'` : '';
367
+ logDocumentationError(moduleDoc.path, `Incorrect use of @${tag.tag}${nodeName}. Ensure it is part of ${fieldType} JSDoc tags.`)
364
368
  }
365
369
 
366
370
  return !!isValid;
@@ -396,7 +400,12 @@ const displayDocumentationErrors = () => {
396
400
  }
397
401
 
398
402
  const formatArrays = (typeText) => {
399
- return typeText?.replaceAll(/(\S+)\[\]/g, "Array<$1>")
403
+ return typeText?.replaceAll(/(\S+)\[\]/g, "Array<$1>");
404
+ }
405
+
406
+ // Convert Slot<T> and DefaultSlot<T> to Array<T> (the array is built into these types)
407
+ const formatSlotTypes = (typeText) => {
408
+ return typeText?.replace(/(Default)?Slot<(.+?)>/g, 'Array<$2>');
400
409
  }
401
410
 
402
411
  export {
@@ -415,6 +424,7 @@ export {
415
424
  getTypeRefs,
416
425
  normalizeDescription,
417
426
  formatArrays,
427
+ formatSlotTypes,
418
428
  isClass,
419
429
  normalizeTagType,
420
430
  displayDocumentationErrors,