From 5702592fd40b9382702cee1a28c1729835092afc Mon Sep 17 00:00:00 2001 From: Davide Mininni <101575400+DavideMininni-Fincons@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:06:58 +0200 Subject: [PATCH] build(docs-generate): override type in docs generation (#3169) --- .../checkbox/checkbox-panel/checkbox-panel.ts | 1 + .../checkbox/checkbox-panel/readme.md | 3 -- src/elements/checkbox/checkbox/checkbox.ts | 1 + src/elements/checkbox/checkbox/readme.md | 3 -- src/elements/file-selector/file-selector.ts | 1 + src/elements/file-selector/readme.md | 3 -- src/elements/select/readme.md | 3 -- src/elements/select/select.ts | 1 + src/elements/slider/readme.md | 3 -- src/elements/slider/slider.ts | 1 + src/elements/toggle-check/readme.md | 3 -- src/elements/toggle-check/toggle-check.ts | 1 + tools/docs/docs_generate.ts | 41 +--------------- .../custom-elements-manifest.config.js | 49 +++++++++++++++++++ 14 files changed, 56 insertions(+), 58 deletions(-) diff --git a/src/elements/checkbox/checkbox-panel/checkbox-panel.ts b/src/elements/checkbox/checkbox-panel/checkbox-panel.ts index 43abf00d6d..ac980e708b 100644 --- a/src/elements/checkbox/checkbox-panel/checkbox-panel.ts +++ b/src/elements/checkbox/checkbox-panel/checkbox-panel.ts @@ -41,6 +41,7 @@ export type SbbCheckboxPanelStateChange = Extract< * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. + * @overrideType value - string | null */ export @customElement('sbb-checkbox-panel') diff --git a/src/elements/checkbox/checkbox-panel/readme.md b/src/elements/checkbox/checkbox-panel/readme.md index 23536063b9..a2a542115b 100644 --- a/src/elements/checkbox/checkbox-panel/readme.md +++ b/src/elements/checkbox/checkbox-panel/readme.md @@ -71,9 +71,6 @@ The component provides the same accessibility features as the native checkbox. Always provide an accessible label via `aria-label` for checkboxes without descriptive text content. If you don't want the label to appear next to the checkbox, you can use `aria-label` to specify an appropriate label. - ## Properties diff --git a/src/elements/checkbox/checkbox/checkbox.ts b/src/elements/checkbox/checkbox/checkbox.ts index 01afd89802..ca07883742 100644 --- a/src/elements/checkbox/checkbox/checkbox.ts +++ b/src/elements/checkbox/checkbox/checkbox.ts @@ -22,6 +22,7 @@ import '../../visual-checkbox.js'; * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. + * @overrideType value - string | null */ export @customElement('sbb-checkbox') diff --git a/src/elements/checkbox/checkbox/readme.md b/src/elements/checkbox/checkbox/readme.md index 9a3e08bbc9..6280437e87 100644 --- a/src/elements/checkbox/checkbox/readme.md +++ b/src/elements/checkbox/checkbox/readme.md @@ -77,9 +77,6 @@ If you don't want the label to appear next to the checkbox, you can use `aria-la ``` - ## Properties diff --git a/src/elements/file-selector/file-selector.ts b/src/elements/file-selector/file-selector.ts index c6d5d6c30d..3736a5e156 100644 --- a/src/elements/file-selector/file-selector.ts +++ b/src/elements/file-selector/file-selector.ts @@ -36,6 +36,7 @@ export type DOMEvent = globalThis.Event; * @event {CustomEvent} fileChanged - An event which is emitted each time the file list changes. * @event change - An event which is emitted each time the user modifies the value. Unlike the input event, the change event is not necessarily fired for each alteration to an element's value * @event input - An event which is emitted each time the value changes as a direct result of a user action. + * @overrideType value - string | null */ export @customElement('sbb-file-selector') diff --git a/src/elements/file-selector/readme.md b/src/elements/file-selector/readme.md index e1cdbc1237..c6842e925d 100644 --- a/src/elements/file-selector/readme.md +++ b/src/elements/file-selector/readme.md @@ -84,9 +84,6 @@ It's suggested to have a different value for each variant, e.g.: > ``` - ## Properties diff --git a/src/elements/select/readme.md b/src/elements/select/readme.md index 2cdabc1467..57679bdc03 100644 --- a/src/elements/select/readme.md +++ b/src/elements/select/readme.md @@ -111,9 +111,6 @@ Opened panel: | ShiftUp Arrow | If `multiple`, moves to the next non-disabled option and toggle its selection. | | Any char or number | If exists, select the first non-disabled matching option after the selected value. | - ## Properties diff --git a/src/elements/select/select.ts b/src/elements/select/select.ts index 0936daaaef..8814654d37 100644 --- a/src/elements/select/select.ts +++ b/src/elements/select/select.ts @@ -53,6 +53,7 @@ export interface SelectChange { * @cssprop [--sbb-select-z-index=var(--sbb-overlay-default-z-index)] - To specify a custom stack order, * the `z-index` can be overridden by defining this CSS variable. The default `z-index` of the * component is set to `var(--sbb-overlay-default-z-index)` with a value of `1000`. + * @overrideType value - string | string[] | null */ export @customElement('sbb-select') diff --git a/src/elements/slider/readme.md b/src/elements/slider/readme.md index 2843463727..8011ad5322 100644 --- a/src/elements/slider/readme.md +++ b/src/elements/slider/readme.md @@ -66,9 +66,6 @@ The `sbb-slider` has the following behaviour on keypress when focused: | End | Set the value to the maximum. | | Home | Set the value to the minimum. | - ## Properties diff --git a/src/elements/slider/slider.ts b/src/elements/slider/slider.ts index 705a334cba..3a2ec4a045 100644 --- a/src/elements/slider/slider.ts +++ b/src/elements/slider/slider.ts @@ -24,6 +24,7 @@ import '../icon.js'; * @slot prefix - Use this slot to render an icon on the left side of the input. * @slot suffix - Use this slot to render an icon on the right side of the input. * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. + * @overrideType value - string | null */ export @customElement('sbb-slider') diff --git a/src/elements/toggle-check/readme.md b/src/elements/toggle-check/readme.md index eaa980a2c8..532d0a7978 100644 --- a/src/elements/toggle-check/readme.md +++ b/src/elements/toggle-check/readme.md @@ -60,9 +60,6 @@ you can not provide it and then use `aria-label` to specify an appropriate label ``` - ## Properties diff --git a/src/elements/toggle-check/toggle-check.ts b/src/elements/toggle-check/toggle-check.ts index 21382b758e..ce38bb1eaf 100644 --- a/src/elements/toggle-check/toggle-check.ts +++ b/src/elements/toggle-check/toggle-check.ts @@ -16,6 +16,7 @@ import style from './toggle-check.scss?lit&inline'; * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. + * @overrideType value - string | null */ export @customElement('sbb-toggle-check') diff --git a/tools/docs/docs_generate.ts b/tools/docs/docs_generate.ts index 4f57913cb0..8edaef1fcf 100644 --- a/tools/docs/docs_generate.ts +++ b/tools/docs/docs_generate.ts @@ -27,7 +27,6 @@ const componentsFolder = './src'; const inheritedFromColumnIndex = 6; const propertyColumnIndex = 1; const attributeColumnIndex = 2; -const typeColumnIndex = 3; function getAttributeName(propertyName: string, attributes: Attribute[]): string { const name = propertyName.replace(/['`]/g, '').trim(); @@ -35,47 +34,12 @@ function getAttributeName(propertyName: string, attributes: Attribute[]): string return attr ? `\`${attr}\`` : '-'; } -/** - * Applies overrides to the fields table. The override rules are to be written in the readme, - * before the autogenerated section, in the following format: - * - * - */ -function applyOverrides(newReadme: MagicString, tableRows: string[][]): void { - const overrideSection = newReadme.original.match(//gm)?.[0]; - if (!overrideSection) { - return; - } - - // Types override (match: @type [property] => [replaceValue]) - const typesOverride = Array.from( - overrideSection.matchAll(/@type (?.*)=>(?.*)/g), - ).map((ov) => ({ - property: ov.groups!.property.trim(), - replace: ov.groups!.replace.trim(), - })); - - typesOverride.forEach((ov) => { - const propRow = tableRows.find( - (row) => row[propertyColumnIndex].trim() === `\`${ov.property}\``, - )!; - if (!propRow) { - throw Error(`[Docs override] Could not find the "${ov.property}" property to override`); - } - propRow[typeColumnIndex] = `\`${ov.replace}\``; - }); -} - /** * Add the 'attribute' column. * Removes the 'Inherited from' column. * Replace 'Fields' title with 'Properties' */ function updateFieldsTable( - newReadme: MagicString, newDocs: MagicString, sections: RegExpMatchArray[], attributes?: Attribute[], @@ -99,9 +63,6 @@ function updateFieldsTable( .map((match) => match[0]) .map((row) => row.split(/(? row.splice(inheritedFromColumnIndex, 1)); @@ -151,7 +112,7 @@ async function updateComponentReadme( // Remove the title newDocs.replace(/^# class: `.*`\n/m, ''); - updateFieldsTable(newReadme, newDocs, sections, manifest.attributes ?? []); + updateFieldsTable(newDocs, sections, manifest.attributes ?? []); newDocs = new MagicString(newDocs.toString()); // Unescape ` diff --git a/tools/manifest/custom-elements-manifest.config.js b/tools/manifest/custom-elements-manifest.config.js index 927f02bdf5..8853907d76 100644 --- a/tools/manifest/custom-elements-manifest.config.js +++ b/tools/manifest/custom-elements-manifest.config.js @@ -1,3 +1,5 @@ +const overrideTypeKey = 'overrideType'; + /** * Docs: https://custom-elements-manifest.open-wc.org/analyzer/getting-started/ */ @@ -34,6 +36,33 @@ export function createManifestConfig(library = '') { classNode = classNode.parent; } } + + /** + * When a generic T type is used in a superclass declaration, it overrides the type defined in derived class + * during the doc generation (as the `value` property in the `SbbFormAssociatedMixinType`). + * Using the `@overrideType` annotation in the jsDoc's derived class allows to override the type with the correct one. + * + * In this phase, the script looks for all the `@overrideType` annotations, + * and it saves them in the `classDeclaration` object as a pair / . + */ + if (node.kind === ts.SyntaxKind.ClassDeclaration) { + node.jsDoc?.forEach((doc) => { + doc.tags?.forEach((tag) => { + // eslint-disable-next-line lyne/local-name-rule + if (tag.tagName.getText() === overrideTypeKey) { + const [memberName, memberOverrideType] = tag.comment.split(' - '); + const classDeclaration = moduleDoc.declarations.find( + (declaration) => declaration.name === node.name.getText(), + ); + if (!classDeclaration[overrideTypeKey]) { + classDeclaration[overrideTypeKey] = [{ memberName, memberOverrideType }]; + } else { + classDeclaration[overrideTypeKey].push({ memberName, memberOverrideType }); + } + } + }); + }); + } }, packageLinkPhase({ customElementsManifest }) { function fixModulePaths(node, pathAction) { @@ -56,6 +85,26 @@ export function createManifestConfig(library = '') { if (declaration.name.includes('Base')) { delete declaration.customElement; } + + /** + * Search for all the `classDeclaration` which have the `overrideTypeKey` property, + * and update the provided property with the provided type. + */ + if (declaration[overrideTypeKey]) { + declaration[overrideTypeKey].forEach((overrideObj) => { + const memberToOverride = declaration.members.find( + (member) => member.name === overrideObj.memberName, + ); + if (memberToOverride) { + memberToOverride.type = { + ...memberToOverride.type, + text: overrideObj.memberOverrideType, + }; + } + }); + delete declaration[overrideTypeKey]; + } + for (const member of declaration.members) { if (member.name.startsWith('_') && member.default) { const publicName = member.name.replace(/^_/, '');