From f93763523a28d6f545050265a2867d03b7305460 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 13 Feb 2025 15:30:42 -0800 Subject: [PATCH 1/7] fix(hydrate): support kebab case in hydrate mode --- src/hydrate/platform/proxy-host-element.ts | 11 +- src/runtime/vdom/set-accessor.ts | 283 +++++++++--------- test/end-to-end/src/components.d.ts | 62 ++++ .../src/hydrate-props/hydrate-props.e2e.ts | 30 ++ test/end-to-end/src/hydrate-props/my-cmp.tsx | 28 ++ .../src/hydrate-props/my-jsx-cmp.tsx | 26 ++ test/end-to-end/src/hydrate-props/readme.md | 19 ++ 7 files changed, 313 insertions(+), 146 deletions(-) create mode 100644 test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts create mode 100644 test/end-to-end/src/hydrate-props/my-cmp.tsx create mode 100644 test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx create mode 100644 test/end-to-end/src/hydrate-props/readme.md diff --git a/src/hydrate/platform/proxy-host-element.ts b/src/hydrate/platform/proxy-host-element.ts index 7c13f5a6123..6db2e14c4d3 100644 --- a/src/hydrate/platform/proxy-host-element.ts +++ b/src/hydrate/platform/proxy-host-element.ts @@ -90,17 +90,18 @@ export function proxyHostElement(elm: d.HostElement, cstr: d.ComponentConstructo } // element - Object.defineProperty(elm, memberName, { - get: function (this: any) { + const getterSetterDescriptor: PropertyDescriptor = { + get: function (this: d.RuntimeRef) { return getValue(this, memberName); }, - set(this: d.RuntimeRef, newValue) { - // proxyComponent, set value + set: function (this: d.RuntimeRef, newValue: unknown) { setValue(this, memberName, newValue, cmpMeta); }, configurable: true, enumerable: true, - }); + }; + Object.defineProperty(elm, memberName, getterSetterDescriptor); + Object.defineProperty(elm, metaAttributeName, getterSetterDescriptor); if (!(cstr as any).prototype.__stencilAugmented) { // instance prototype diff --git a/src/runtime/vdom/set-accessor.ts b/src/runtime/vdom/set-accessor.ts index 53b7e755ee0..dbe88bc254a 100644 --- a/src/runtime/vdom/set-accessor.ts +++ b/src/runtime/vdom/set-accessor.ts @@ -39,168 +39,169 @@ export const setAccessor = ( flags: number, initialRender?: boolean, ) => { - if (oldValue !== newValue) { - let isProp = isMemberInElement(elm, memberName); - let ln = memberName.toLowerCase(); - - if (BUILD.vdomClass && memberName === 'class') { - const classList = elm.classList; - const oldClasses = parseClassList(oldValue); - let newClasses = parseClassList(newValue); + if (oldValue === newValue) { + return + } - if (BUILD.hydrateClientSide && elm['s-si'] && initialRender) { - // for `scoped: true` components, new nodes after initial hydration - // from SSR don't have the slotted class added. Let's add that now - newClasses.push(elm['s-si']); - oldClasses.forEach((c) => { - if (c.startsWith(elm['s-si'])) newClasses.push(c); - }); - newClasses = [...new Set(newClasses)]; - classList.add(...newClasses); - } else { - classList.remove(...oldClasses.filter((c) => c && !newClasses.includes(c))); - classList.add(...newClasses.filter((c) => c && !oldClasses.includes(c))); - } - } else if (BUILD.vdomStyle && memberName === 'style') { - // update style attribute, css properties and values - if (BUILD.updatable) { - for (const prop in oldValue) { - if (!newValue || newValue[prop] == null) { - if (!BUILD.hydrateServerSide && prop.includes('-')) { - elm.style.removeProperty(prop); - } else { - (elm as any).style[prop] = ''; - } - } - } - } + let isProp = isMemberInElement(elm, memberName); + let ln = memberName.toLowerCase(); + if (BUILD.vdomClass && memberName === 'class') { + const classList = elm.classList; + const oldClasses = parseClassList(oldValue); + let newClasses = parseClassList(newValue); - for (const prop in newValue) { - if (!oldValue || newValue[prop] !== oldValue[prop]) { + if (BUILD.hydrateClientSide && elm['s-si'] && initialRender) { + // for `scoped: true` components, new nodes after initial hydration + // from SSR don't have the slotted class added. Let's add that now + newClasses.push(elm['s-si']); + oldClasses.forEach((c) => { + if (c.startsWith(elm['s-si'])) newClasses.push(c); + }); + newClasses = [...new Set(newClasses)]; + classList.add(...newClasses); + } else { + classList.remove(...oldClasses.filter((c) => c && !newClasses.includes(c))); + classList.add(...newClasses.filter((c) => c && !oldClasses.includes(c))); + } + } else if (BUILD.vdomStyle && memberName === 'style') { + // update style attribute, css properties and values + if (BUILD.updatable) { + for (const prop in oldValue) { + if (!newValue || newValue[prop] == null) { if (!BUILD.hydrateServerSide && prop.includes('-')) { - elm.style.setProperty(prop, newValue[prop]); + elm.style.removeProperty(prop); } else { - (elm as any).style[prop] = newValue[prop]; + (elm as any).style[prop] = ''; } } } - } else if (BUILD.vdomKey && memberName === 'key') { - // minifier will clean this up - } else if (BUILD.vdomRef && memberName === 'ref') { - // minifier will clean this up - if (newValue) { - newValue(elm); - } - } else if ( - BUILD.vdomListener && - (BUILD.lazyLoad ? !isProp : !(elm as any).__lookupSetter__(memberName)) && - memberName[0] === 'o' && - memberName[1] === 'n' - ) { - // Event Handlers - // so if the member name starts with "on" and the 3rd characters is - // a capital letter, and it's not already a member on the element, - // then we're assuming it's an event listener - if (memberName[2] === '-') { - // on- prefixed events - // allows to be explicit about the dom event to listen without any magic - // under the hood: - // // listens for "click" - // // listens for "Click" - // // listens for "ionChange" - // // listens for "EVENTS" - memberName = memberName.slice(3); - } else if (isMemberInElement(win, ln)) { - // standard event - // the JSX attribute could have been "onMouseOver" and the - // member name "onmouseover" is on the window's prototype - // so let's add the listener "mouseover", which is all lowercased - memberName = ln.slice(2); - } else { - // custom event - // the JSX attribute could have been "onMyCustomEvent" - // so let's trim off the "on" prefix and lowercase the first character - // and add the listener "myCustomEvent" - // except for the first character, we keep the event name case - memberName = ln[2] + memberName.slice(3); - } - if (oldValue || newValue) { - // Need to account for "capture" events. - // If the event name ends with "Capture", we'll update the name to remove - // the "Capture" suffix and make sure the event listener is setup to handle the capture event. - const capture = memberName.endsWith(CAPTURE_EVENT_SUFFIX); - // Make sure we only replace the last instance of "Capture" - memberName = memberName.replace(CAPTURE_EVENT_REGEX, ''); + } - if (oldValue) { - plt.rel(elm, memberName, oldValue, capture); - } - if (newValue) { - plt.ael(elm, memberName, newValue, capture); + for (const prop in newValue) { + if (!oldValue || newValue[prop] !== oldValue[prop]) { + if (!BUILD.hydrateServerSide && prop.includes('-')) { + elm.style.setProperty(prop, newValue[prop]); + } else { + (elm as any).style[prop] = newValue[prop]; } } - } else if (BUILD.vdomPropOrAttr) { - // Set property if it exists and it's not a SVG - const isComplex = isComplexType(newValue); - if ((isProp || (isComplex && newValue !== null)) && !isSvg) { - try { - if (!elm.tagName.includes('-')) { - const n = newValue == null ? '' : newValue; + } + } else if (BUILD.vdomKey && memberName === 'key') { + // minifier will clean this up + } else if (BUILD.vdomRef && memberName === 'ref') { + // minifier will clean this up + if (newValue) { + newValue(elm); + } + } else if ( + BUILD.vdomListener && + (BUILD.lazyLoad ? !isProp : !(elm as any).__lookupSetter__(memberName)) && + memberName[0] === 'o' && + memberName[1] === 'n' + ) { + // Event Handlers + // so if the member name starts with "on" and the 3rd characters is + // a capital letter, and it's not already a member on the element, + // then we're assuming it's an event listener + if (memberName[2] === '-') { + // on- prefixed events + // allows to be explicit about the dom event to listen without any magic + // under the hood: + // // listens for "click" + // // listens for "Click" + // // listens for "ionChange" + // // listens for "EVENTS" + memberName = memberName.slice(3); + } else if (isMemberInElement(win, ln)) { + // standard event + // the JSX attribute could have been "onMouseOver" and the + // member name "onmouseover" is on the window's prototype + // so let's add the listener "mouseover", which is all lowercased + memberName = ln.slice(2); + } else { + // custom event + // the JSX attribute could have been "onMyCustomEvent" + // so let's trim off the "on" prefix and lowercase the first character + // and add the listener "myCustomEvent" + // except for the first character, we keep the event name case + memberName = ln[2] + memberName.slice(3); + } + if (oldValue || newValue) { + // Need to account for "capture" events. + // If the event name ends with "Capture", we'll update the name to remove + // the "Capture" suffix and make sure the event listener is setup to handle the capture event. + const capture = memberName.endsWith(CAPTURE_EVENT_SUFFIX); + // Make sure we only replace the last instance of "Capture" + memberName = memberName.replace(CAPTURE_EVENT_REGEX, ''); + + if (oldValue) { + plt.rel(elm, memberName, oldValue, capture); + } + if (newValue) { + plt.ael(elm, memberName, newValue, capture); + } + } + } else if (BUILD.vdomPropOrAttr) { + // Set property if it exists and it's not a SVG + const isComplex = isComplexType(newValue); + if ((isProp || (isComplex && newValue !== null)) && !isSvg) { + try { + if (!elm.tagName.includes('-')) { + const n = newValue == null ? '' : newValue; - // Workaround for Safari, moving the caret when re-assigning the same valued - if (memberName === 'list') { - isProp = false; - } else if (oldValue == null || (elm as any)[memberName] != n) { - if (typeof (elm as any).__lookupSetter__(memberName) === 'function') { - (elm as any)[memberName] = n; - } else { - elm.setAttribute(memberName, n); - } + // Workaround for Safari, moving the caret when re-assigning the same valued + if (memberName === 'list') { + isProp = false; + } else if (oldValue == null || (elm as any)[memberName] != n) { + if (typeof (elm as any).__lookupSetter__(memberName) === 'function') { + (elm as any)[memberName] = n; + } else { + elm.setAttribute(memberName, n); } - } else if ((elm as any)[memberName] !== newValue) { - (elm as any)[memberName] = newValue; } - } catch (e) { - /** - * in case someone tries to set a read-only property, e.g. "namespaceURI", we just ignore it - */ + } else if ((elm as any)[memberName] !== newValue) { + (elm as any)[memberName] = newValue; } + } catch (e) { + /** + * in case someone tries to set a read-only property, e.g. "namespaceURI", we just ignore it + */ } + } - /** - * Need to manually update attribute if: - * - memberName is not an attribute - * - if we are rendering the host element in order to reflect attribute - * - if it's a SVG, since properties might not work in - * - if the newValue is null/undefined or 'false'. - */ - let xlink = false; - if (BUILD.vdomXlink) { - if (ln !== (ln = ln.replace(/^xlink\:?/, ''))) { - memberName = ln; - xlink = true; - } + /** + * Need to manually update attribute if: + * - memberName is not an attribute + * - if we are rendering the host element in order to reflect attribute + * - if it's a SVG, since properties might not work in + * - if the newValue is null/undefined or 'false'. + */ + let xlink = false; + if (BUILD.vdomXlink) { + if (ln !== (ln = ln.replace(/^xlink\:?/, ''))) { + memberName = ln; + xlink = true; } - if (newValue == null || newValue === false) { - if (newValue !== false || elm.getAttribute(memberName) === '') { - if (BUILD.vdomXlink && xlink) { - elm.removeAttributeNS(XLINK_NS, memberName); - } else { - elm.removeAttribute(memberName); - } - } - } else if ( - (!isProp || flags & VNODE_FLAGS.isHost || isSvg) && - !isComplex && - elm.nodeType === NODE_TYPE.ElementNode - ) { - newValue = newValue === true ? '' : newValue; + } + if (newValue == null || newValue === false) { + if (newValue !== false || elm.getAttribute(memberName) === '') { if (BUILD.vdomXlink && xlink) { - elm.setAttributeNS(XLINK_NS, memberName, newValue); + elm.removeAttributeNS(XLINK_NS, memberName); } else { - elm.setAttribute(memberName, newValue); + elm.removeAttribute(memberName); } } + } else if ( + (!isProp || flags & VNODE_FLAGS.isHost || isSvg) && + !isComplex && + elm.nodeType === NODE_TYPE.ElementNode + ) { + newValue = newValue === true ? '' : newValue; + if (BUILD.vdomXlink && xlink) { + elm.setAttributeNS(XLINK_NS, memberName, newValue); + } else { + elm.setAttribute(memberName, newValue); + } } } }; diff --git a/test/end-to-end/src/components.d.ts b/test/end-to-end/src/components.d.ts index 8250e4642b7..02dedd36b5a 100644 --- a/test/end-to-end/src/components.d.ts +++ b/test/end-to-end/src/components.d.ts @@ -102,6 +102,28 @@ export namespace Components { "someMethodWithArgs": (unit: string, value: number) => Promise; "someProp": number; } + interface MyCmp { + /** + * @readonly + */ + "barProp": string; + "fooProp": string; + /** + * Mode + */ + "mode"?: any; + } + interface MyJsxCmp { + /** + * @readonly + */ + "barProp": string; + "fooProp": string; + /** + * Mode + */ + "mode"?: any; + } interface NestedCmpChild { } interface NestedCmpParent { @@ -370,6 +392,18 @@ declare global { prototype: HTMLMethodCmpElement; new (): HTMLMethodCmpElement; }; + interface HTMLMyCmpElement extends Components.MyCmp, HTMLStencilElement { + } + var HTMLMyCmpElement: { + prototype: HTMLMyCmpElement; + new (): HTMLMyCmpElement; + }; + interface HTMLMyJsxCmpElement extends Components.MyJsxCmp, HTMLStencilElement { + } + var HTMLMyJsxCmpElement: { + prototype: HTMLMyJsxCmpElement; + new (): HTMLMyJsxCmpElement; + }; interface HTMLNestedCmpChildElement extends Components.NestedCmpChild, HTMLStencilElement { } var HTMLNestedCmpChildElement: { @@ -524,6 +558,8 @@ declare global { "import-assets": HTMLImportAssetsElement; "listen-cmp": HTMLListenCmpElement; "method-cmp": HTMLMethodCmpElement; + "my-cmp": HTMLMyCmpElement; + "my-jsx-cmp": HTMLMyJsxCmpElement; "nested-cmp-child": HTMLNestedCmpChildElement; "nested-cmp-parent": HTMLNestedCmpParentElement; "nested-scope-cmp": HTMLNestedScopeCmpElement; @@ -616,6 +652,28 @@ declare namespace LocalJSX { interface MethodCmp { "someProp"?: number; } + interface MyCmp { + /** + * @readonly + */ + "barProp"?: string; + "fooProp"?: string; + /** + * Mode + */ + "mode"?: any; + } + interface MyJsxCmp { + /** + * @readonly + */ + "barProp"?: string; + "fooProp"?: string; + /** + * Mode + */ + "mode"?: any; + } interface NestedCmpChild { } interface NestedCmpParent { @@ -702,6 +760,8 @@ declare namespace LocalJSX { "import-assets": ImportAssets; "listen-cmp": ListenCmp; "method-cmp": MethodCmp; + "my-cmp": MyCmp; + "my-jsx-cmp": MyJsxCmp; "nested-cmp-child": NestedCmpChild; "nested-cmp-parent": NestedCmpParent; "nested-scope-cmp": NestedScopeCmp; @@ -758,6 +818,8 @@ declare module "@stencil/core" { "import-assets": LocalJSX.ImportAssets & JSXBase.HTMLAttributes; "listen-cmp": LocalJSX.ListenCmp & JSXBase.HTMLAttributes; "method-cmp": LocalJSX.MethodCmp & JSXBase.HTMLAttributes; + "my-cmp": LocalJSX.MyCmp & JSXBase.HTMLAttributes; + "my-jsx-cmp": LocalJSX.MyJsxCmp & JSXBase.HTMLAttributes; "nested-cmp-child": LocalJSX.NestedCmpChild & JSXBase.HTMLAttributes; "nested-cmp-parent": LocalJSX.NestedCmpParent & JSXBase.HTMLAttributes; "nested-scope-cmp": LocalJSX.NestedScopeCmp & JSXBase.HTMLAttributes; diff --git a/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts new file mode 100644 index 00000000000..53438a7b34f --- /dev/null +++ b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts @@ -0,0 +1,30 @@ +type HydrateModule = typeof import('../../hydrate'); +let renderToString: HydrateModule['renderToString']; + +describe('different types of decorated properties and states render on both server and client', () => { + + beforeAll(async () => { + // @ts-ignore may not be existing when project hasn't been built + const mod = await import('../../hydrate'); + renderToString = mod.renderToString; + }); + + it('renders default values', async () => { + const { html } = await renderToString(` + + + `, { + fullDocument: false, + }); + // html template renders kebab case props + expect(html).toContain('foo1 - bar1<') + // html template doesn't support camelcase + expect(html).toContain(' - bar<') + // jsx template renders kebab case + expect(html).toContain('foo3 - bar3<') + expect(html).toContain('foo3 - bar3<') + // jsx template renders camel case + expect(html).toContain('foo4 - bar4<') + expect(html).toContain('foo4 - bar4<') + }); +}); diff --git a/test/end-to-end/src/hydrate-props/my-cmp.tsx b/test/end-to-end/src/hydrate-props/my-cmp.tsx new file mode 100644 index 00000000000..bd46d27ab6c --- /dev/null +++ b/test/end-to-end/src/hydrate-props/my-cmp.tsx @@ -0,0 +1,28 @@ +import { Component, h, Prop } from '@stencil/core'; + +/** + * @virtualProp mode - Mode + */ +@Component({ + tag: 'my-cmp', + shadow: true, +}) +export class MyCmp { + @Prop() + fooProp: string; + + @Prop() + get barProp() { + return 'bar'; + } + + render() { + return ( +
+ {this.fooProp} - {this.barProp} + + +
+ ); + } +} diff --git a/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx b/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx new file mode 100644 index 00000000000..23cf39cd786 --- /dev/null +++ b/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx @@ -0,0 +1,26 @@ +import { Component, h, Prop } from '@stencil/core'; + +/** + * @virtualProp mode - Mode + */ +@Component({ + tag: 'my-jsx-cmp', + shadow: true, +}) +export class MyJsxCmp { + @Prop() + fooProp: string; + + @Prop() + get barProp() { + return 'bar'; + } + + render() { + return ( +
+ {this.fooProp} - {this.barProp} +
+ ); + } +} diff --git a/test/end-to-end/src/hydrate-props/readme.md b/test/end-to-end/src/hydrate-props/readme.md new file mode 100644 index 00000000000..c9f0a6e9913 --- /dev/null +++ b/test/end-to-end/src/hydrate-props/readme.md @@ -0,0 +1,19 @@ +# hydrate-props + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| --------- | ---------- | ----------- | -------- | ----------- | +| `barProp` | `bar-prop` | | `string` | `'bar'` | +| `fooProp` | `foo-prop` | | `string` | `undefined` | +| `mode` | `mode` | Mode | `any` | `undefined` | + + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* From e3c1554ca179e44fde036ef3daa75218881a8490 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 13 Feb 2025 16:35:04 -0800 Subject: [PATCH 2/7] deprecate usage of dash-casing in JSX --- .../types/generate-component-types.ts | 14 +++ src/compiler/types/generate-prop-types.ts | 1 + src/declarations/stencil-private.ts | 1 + test/end-to-end/src/components.d.ts | 108 ++++++++++++++++++ test/end-to-end/src/hydrate-props/my-cmp.tsx | 7 ++ .../src/hydrate-props/my-jsx-cmp.tsx | 7 ++ test/wdio/src/components.d.ts | 8 ++ test/wdio/ts-target-props/components.d.ts | 24 ++++ 8 files changed, 170 insertions(+) diff --git a/src/compiler/types/generate-component-types.ts b/src/compiler/types/generate-component-types.ts index 04e015ae693..3195d083654 100644 --- a/src/compiler/types/generate-component-types.ts +++ b/src/compiler/types/generate-component-types.ts @@ -76,6 +76,20 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool } const optional = jsxAttributes ? !type.required : type.optional; fullList.push(` "${type.name}"${optional ? '?' : ''}: ${type.type};`); + + /** + * deprecated usage of dash-casing in JSX, use camelCase instead + */ + if (type.attributeName && type.attributeName !== type.name) { + const padding = ' '.repeat(8); + fullList.push([ + `${padding}/**`, + `${padding} * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5.`, + `${padding} */` + ].join('\n')); + fullList.push(`${padding}"${type.attributeName}"${optional ? '?' : ''}: ${type.type};`); + } + return fullList; }, [] as string[]) .join(`\n`); diff --git a/src/compiler/types/generate-prop-types.ts b/src/compiler/types/generate-prop-types.ts index bb8ebbf25a2..442a721f647 100644 --- a/src/compiler/types/generate-prop-types.ts +++ b/src/compiler/types/generate-prop-types.ts @@ -20,6 +20,7 @@ export const generatePropTypes = (cmpMeta: d.ComponentCompilerMeta, typeImportDa } return { name: cmpProp.name, + attributeName: cmpProp.attribute, type: getType(cmpProp, typeImportData, cmpMeta.sourceFilePath), optional: cmpProp.optional, required: cmpProp.required, diff --git a/src/declarations/stencil-private.ts b/src/declarations/stencil-private.ts index 6ff17da01e9..1f1000a8b35 100644 --- a/src/declarations/stencil-private.ts +++ b/src/declarations/stencil-private.ts @@ -2565,6 +2565,7 @@ export interface TypesModule { export type TypeInfo = { name: string; type: string; + attributeName?: string; optional: boolean; required: boolean; internal: boolean; diff --git a/test/end-to-end/src/components.d.ts b/test/end-to-end/src/components.d.ts index 02dedd36b5a..9f9f7d61500 100644 --- a/test/end-to-end/src/components.d.ts +++ b/test/end-to-end/src/components.d.ts @@ -40,6 +40,10 @@ export namespace Components { } interface CmpDsd { "initialCounter": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "initial-counter": number; } interface CmpServerVsClient { } @@ -101,13 +105,30 @@ export namespace Components { */ "someMethodWithArgs": (unit: string, value: number) => Promise; "someProp": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "some-prop": number; } interface MyCmp { /** + * bar prop + * @returns bar * @readonly */ "barProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "bar-prop": string; + /** + * foo prop + */ "fooProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "foo-prop": string; /** * Mode */ @@ -115,10 +136,23 @@ export namespace Components { } interface MyJsxCmp { /** + * bar prop + * @returns bar * @readonly */ "barProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "bar-prop": string; + /** + * foo prop + */ "fooProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "foo-prop": string; /** * Mode */ @@ -149,7 +183,15 @@ export namespace Components { * @readonly */ "fullName": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "full-name": string; "lastName": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "last-name": string; /** * Mode */ @@ -157,8 +199,20 @@ export namespace Components { } interface RuntimeDecorators { "basicProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "basic-prop": string; "decoratedGetterSetterProp": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-getter-setter-prop": number; "decoratedProp": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-prop": number; } interface ScopedCarDetail { "car": CarData; @@ -616,6 +670,10 @@ declare namespace LocalJSX { } interface CmpDsd { "initialCounter"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "initial-counter"?: number; } interface CmpServerVsClient { } @@ -651,13 +709,30 @@ declare namespace LocalJSX { } interface MethodCmp { "someProp"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "some-prop"?: number; } interface MyCmp { /** + * bar prop + * @returns bar * @readonly */ "barProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "bar-prop"?: string; + /** + * foo prop + */ "fooProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "foo-prop"?: string; /** * Mode */ @@ -665,10 +740,23 @@ declare namespace LocalJSX { } interface MyJsxCmp { /** + * bar prop + * @returns bar * @readonly */ "barProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "bar-prop"?: string; + /** + * foo prop + */ "fooProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "foo-prop"?: string; /** * Mode */ @@ -699,7 +787,15 @@ declare namespace LocalJSX { * @readonly */ "fullName"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "full-name"?: string; "lastName"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "last-name"?: string; /** * Mode */ @@ -707,8 +803,20 @@ declare namespace LocalJSX { } interface RuntimeDecorators { "basicProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "basic-prop"?: string; "decoratedGetterSetterProp"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-getter-setter-prop"?: number; "decoratedProp"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-prop"?: number; } interface ScopedCarDetail { "car"?: CarData; diff --git a/test/end-to-end/src/hydrate-props/my-cmp.tsx b/test/end-to-end/src/hydrate-props/my-cmp.tsx index bd46d27ab6c..5b23bf7283a 100644 --- a/test/end-to-end/src/hydrate-props/my-cmp.tsx +++ b/test/end-to-end/src/hydrate-props/my-cmp.tsx @@ -8,9 +8,16 @@ import { Component, h, Prop } from '@stencil/core'; shadow: true, }) export class MyCmp { + /** + * foo prop + */ @Prop() fooProp: string; + /** + * bar prop + * @returns bar + */ @Prop() get barProp() { return 'bar'; diff --git a/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx b/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx index 23cf39cd786..b21e204fd87 100644 --- a/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx +++ b/test/end-to-end/src/hydrate-props/my-jsx-cmp.tsx @@ -8,9 +8,16 @@ import { Component, h, Prop } from '@stencil/core'; shadow: true, }) export class MyJsxCmp { + /** + * foo prop + */ @Prop() fooProp: string; + /** + * bar prop + * @returns bar + */ @Prop() get barProp() { return 'bar'; diff --git a/test/wdio/src/components.d.ts b/test/wdio/src/components.d.ts index 187e93e56ee..837ea17a1ec 100644 --- a/test/wdio/src/components.d.ts +++ b/test/wdio/src/components.d.ts @@ -20,6 +20,10 @@ export namespace Components { } interface CmpD { "uniqueId": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "unique-id": string; } interface CmpScopedA { } @@ -135,6 +139,10 @@ declare namespace LocalJSX { } interface CmpD { "uniqueId"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "unique-id"?: string; } interface CmpScopedA { } diff --git a/test/wdio/ts-target-props/components.d.ts b/test/wdio/ts-target-props/components.d.ts index 35205054aa0..d9df961d7a5 100644 --- a/test/wdio/ts-target-props/components.d.ts +++ b/test/wdio/ts-target-props/components.d.ts @@ -8,8 +8,20 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; export namespace Components { interface TsTargetProps { "basicProp": string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "basic-prop": string; "decoratedGetterSetterProp": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-getter-setter-prop": number; "decoratedProp": number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-prop": number; } } declare global { @@ -26,8 +38,20 @@ declare global { declare namespace LocalJSX { interface TsTargetProps { "basicProp"?: string; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "basic-prop"?: string; "decoratedGetterSetterProp"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-getter-setter-prop"?: number; "decoratedProp"?: number; + /** + * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + */ + "decorated-prop"?: number; } interface IntrinsicElements { "ts-target-props": TsTargetProps; From 8e77cc09ce70ff07598e4aba7c0276184cf6504d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 13 Feb 2025 16:43:05 -0800 Subject: [PATCH 3/7] better deprecation message --- .../types/generate-component-types.ts | 2 +- test/end-to-end/src/components.d.ts | 44 +++++++++---------- test/end-to-end/src/hydrate-props/readme.md | 17 ++++++- test/wdio/src/components.d.ts | 4 +- test/wdio/ts-target-props/components.d.ts | 12 ++--- 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/compiler/types/generate-component-types.ts b/src/compiler/types/generate-component-types.ts index 3195d083654..a611e8c7f58 100644 --- a/src/compiler/types/generate-component-types.ts +++ b/src/compiler/types/generate-component-types.ts @@ -84,7 +84,7 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool const padding = ' '.repeat(8); fullList.push([ `${padding}/**`, - `${padding} * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5.`, + `${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`, `${padding} */` ].join('\n')); fullList.push(`${padding}"${type.attributeName}"${optional ? '?' : ''}: ${type.type};`); diff --git a/test/end-to-end/src/components.d.ts b/test/end-to-end/src/components.d.ts index 9f9f7d61500..681eeaa2a33 100644 --- a/test/end-to-end/src/components.d.ts +++ b/test/end-to-end/src/components.d.ts @@ -41,7 +41,7 @@ export namespace Components { interface CmpDsd { "initialCounter": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "initial-counter": number; } @@ -106,7 +106,7 @@ export namespace Components { "someMethodWithArgs": (unit: string, value: number) => Promise; "someProp": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "some-prop": number; } @@ -118,7 +118,7 @@ export namespace Components { */ "barProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "bar-prop": string; /** @@ -126,7 +126,7 @@ export namespace Components { */ "fooProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "foo-prop": string; /** @@ -142,7 +142,7 @@ export namespace Components { */ "barProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "bar-prop": string; /** @@ -150,7 +150,7 @@ export namespace Components { */ "fooProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "foo-prop": string; /** @@ -184,12 +184,12 @@ export namespace Components { */ "fullName": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "full-name": string; "lastName": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "last-name": string; /** @@ -200,17 +200,17 @@ export namespace Components { interface RuntimeDecorators { "basicProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "basic-prop": string; "decoratedGetterSetterProp": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-getter-setter-prop": number; "decoratedProp": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-prop": number; } @@ -671,7 +671,7 @@ declare namespace LocalJSX { interface CmpDsd { "initialCounter"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "initial-counter"?: number; } @@ -710,7 +710,7 @@ declare namespace LocalJSX { interface MethodCmp { "someProp"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "some-prop"?: number; } @@ -722,7 +722,7 @@ declare namespace LocalJSX { */ "barProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "bar-prop"?: string; /** @@ -730,7 +730,7 @@ declare namespace LocalJSX { */ "fooProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "foo-prop"?: string; /** @@ -746,7 +746,7 @@ declare namespace LocalJSX { */ "barProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "bar-prop"?: string; /** @@ -754,7 +754,7 @@ declare namespace LocalJSX { */ "fooProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "foo-prop"?: string; /** @@ -788,12 +788,12 @@ declare namespace LocalJSX { */ "fullName"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "full-name"?: string; "lastName"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "last-name"?: string; /** @@ -804,17 +804,17 @@ declare namespace LocalJSX { interface RuntimeDecorators { "basicProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "basic-prop"?: string; "decoratedGetterSetterProp"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-getter-setter-prop"?: number; "decoratedProp"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-prop"?: number; } diff --git a/test/end-to-end/src/hydrate-props/readme.md b/test/end-to-end/src/hydrate-props/readme.md index c9f0a6e9913..da3a7181cab 100644 --- a/test/end-to-end/src/hydrate-props/readme.md +++ b/test/end-to-end/src/hydrate-props/readme.md @@ -9,11 +9,24 @@ | Property | Attribute | Description | Type | Default | | --------- | ---------- | ----------- | -------- | ----------- | -| `barProp` | `bar-prop` | | `string` | `'bar'` | -| `fooProp` | `foo-prop` | | `string` | `undefined` | +| `barProp` | `bar-prop` | bar prop | `string` | `'bar'` | +| `fooProp` | `foo-prop` | foo prop | `string` | `undefined` | | `mode` | `mode` | Mode | `any` | `undefined` | +## Dependencies + +### Used by + + - [my-cmp](.) + +### Graph +```mermaid +graph TD; + my-cmp --> my-jsx-cmp + style my-jsx-cmp fill:#f9f,stroke:#333,stroke-width:4px +``` + ---------------------------------------------- *Built with [StencilJS](https://stenciljs.com/)* diff --git a/test/wdio/src/components.d.ts b/test/wdio/src/components.d.ts index 837ea17a1ec..bf58ad21c89 100644 --- a/test/wdio/src/components.d.ts +++ b/test/wdio/src/components.d.ts @@ -21,7 +21,7 @@ export namespace Components { interface CmpD { "uniqueId": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "unique-id": string; } @@ -140,7 +140,7 @@ declare namespace LocalJSX { interface CmpD { "uniqueId"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "unique-id"?: string; } diff --git a/test/wdio/ts-target-props/components.d.ts b/test/wdio/ts-target-props/components.d.ts index d9df961d7a5..25c0a828000 100644 --- a/test/wdio/ts-target-props/components.d.ts +++ b/test/wdio/ts-target-props/components.d.ts @@ -9,17 +9,17 @@ export namespace Components { interface TsTargetProps { "basicProp": string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "basic-prop": string; "decoratedGetterSetterProp": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-getter-setter-prop": number; "decoratedProp": number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-prop": number; } @@ -39,17 +39,17 @@ declare namespace LocalJSX { interface TsTargetProps { "basicProp"?: string; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "basic-prop"?: string; "decoratedGetterSetterProp"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-getter-setter-prop"?: number; "decoratedProp"?: number; /** - * @deprecated dash-casing is not supported in JSX, use camelCase instead. Support for it will be removed in Stencil v5. + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ "decorated-prop"?: number; } From 07e43b02d9e349634809fb6748b4b00dae8b0abb Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 13 Feb 2025 16:44:59 -0800 Subject: [PATCH 4/7] prettier --- .../types/generate-component-types.ts | 12 ++++++---- .../src/hydrate-props/hydrate-props.e2e.ts | 24 ++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/compiler/types/generate-component-types.ts b/src/compiler/types/generate-component-types.ts index a611e8c7f58..0a93cbd468a 100644 --- a/src/compiler/types/generate-component-types.ts +++ b/src/compiler/types/generate-component-types.ts @@ -82,11 +82,13 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool */ if (type.attributeName && type.attributeName !== type.name) { const padding = ' '.repeat(8); - fullList.push([ - `${padding}/**`, - `${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`, - `${padding} */` - ].join('\n')); + fullList.push( + [ + `${padding}/**`, + `${padding} * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5.`, + `${padding} */`, + ].join('\n'), + ); fullList.push(`${padding}"${type.attributeName}"${optional ? '?' : ''}: ${type.type};`); } diff --git a/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts index 53438a7b34f..54ad5340999 100644 --- a/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts +++ b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts @@ -2,7 +2,6 @@ type HydrateModule = typeof import('../../hydrate'); let renderToString: HydrateModule['renderToString']; describe('different types of decorated properties and states render on both server and client', () => { - beforeAll(async () => { // @ts-ignore may not be existing when project hasn't been built const mod = await import('../../hydrate'); @@ -10,21 +9,24 @@ describe('different types of decorated properties and states render on both serv }); it('renders default values', async () => { - const { html } = await renderToString(` + const { html } = await renderToString( + ` - `, { - fullDocument: false, - }); + `, + { + fullDocument: false, + }, + ); // html template renders kebab case props - expect(html).toContain('foo1 - bar1<') + expect(html).toContain('foo1 - bar1<'); // html template doesn't support camelcase - expect(html).toContain(' - bar<') + expect(html).toContain(' - bar<'); // jsx template renders kebab case - expect(html).toContain('foo3 - bar3<') - expect(html).toContain('foo3 - bar3<') + expect(html).toContain('foo3 - bar3<'); + expect(html).toContain('foo3 - bar3<'); // jsx template renders camel case - expect(html).toContain('foo4 - bar4<') - expect(html).toContain('foo4 - bar4<') + expect(html).toContain('foo4 - bar4<'); + expect(html).toContain('foo4 - bar4<'); }); }); From 911a348b7ebf10ecb033685e59bae6a8370e4dee Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 20 Feb 2025 17:03:48 -0800 Subject: [PATCH 5/7] prettier --- src/runtime/vdom/set-accessor.ts | 2 +- test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime/vdom/set-accessor.ts b/src/runtime/vdom/set-accessor.ts index dbe88bc254a..074afa815e2 100644 --- a/src/runtime/vdom/set-accessor.ts +++ b/src/runtime/vdom/set-accessor.ts @@ -40,7 +40,7 @@ export const setAccessor = ( initialRender?: boolean, ) => { if (oldValue === newValue) { - return + return; } let isProp = isMemberInElement(elm, memberName); diff --git a/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts index 54ad5340999..141dc6a8eac 100644 --- a/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts +++ b/test/end-to-end/src/hydrate-props/hydrate-props.e2e.ts @@ -1,3 +1,4 @@ +// @ts-ignore may not be existing when project hasn't been built type HydrateModule = typeof import('../../hydrate'); let renderToString: HydrateModule['renderToString']; From 51b0e223fbf328f5316cb49bb14727624d92f5db Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 20 Feb 2025 17:19:55 -0800 Subject: [PATCH 6/7] fix unit test --- src/compiler/types/tests/generate-prop-types.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/types/tests/generate-prop-types.spec.ts b/src/compiler/types/tests/generate-prop-types.spec.ts index 75ef6d78dda..955efec4099 100644 --- a/src/compiler/types/tests/generate-prop-types.spec.ts +++ b/src/compiler/types/tests/generate-prop-types.spec.ts @@ -44,6 +44,7 @@ describe('generate-prop-types', () => { const expectedTypeInfo: d.TypeInfo = [ { + attributeName: 'my-cmp', jsdoc: '', internal: false, name: 'propName', @@ -69,6 +70,7 @@ describe('generate-prop-types', () => { const expectedTypeInfo: d.TypeInfo = [ { + attributeName: 'my-cmp', jsdoc: '', internal: false, name: 'propName', @@ -114,6 +116,7 @@ describe('generate-prop-types', () => { const expectedTypeInfo: d.TypeInfo = [ { + attributeName: 'my-cmp', jsdoc: '', internal: false, name: 'propName', @@ -149,6 +152,7 @@ describe('generate-prop-types', () => { const expectedTypeInfo: d.TypeInfo = [ { + attributeName: 'my-cmp', jsdoc: '@readonly', internal: false, name: 'propName', @@ -176,6 +180,7 @@ describe('generate-prop-types', () => { const expectedTypeInfo: d.TypeInfo = [ { + attributeName: 'my-cmp', jsdoc: '', internal: false, name: 'propName', From 06f85fd133518a06b490559e12912b3299f86c23 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 20 Feb 2025 17:55:55 -0800 Subject: [PATCH 7/7] make deprecated properties optional --- .../types/generate-component-types.ts | 2 +- .../types/tests/generate-app-types.spec.ts | 106 ++++++++++++++++++ test/end-to-end/src/components.d.ts | 22 ++-- test/wdio/src/components.d.ts | 2 +- test/wdio/ts-target-props/components.d.ts | 6 +- 5 files changed, 122 insertions(+), 16 deletions(-) diff --git a/src/compiler/types/generate-component-types.ts b/src/compiler/types/generate-component-types.ts index 0a93cbd468a..30bfc96f5ec 100644 --- a/src/compiler/types/generate-component-types.ts +++ b/src/compiler/types/generate-component-types.ts @@ -89,7 +89,7 @@ const attributesToMultiLineString = (attributes: d.TypeInfo, jsxAttributes: bool `${padding} */`, ].join('\n'), ); - fullList.push(`${padding}"${type.attributeName}"${optional ? '?' : ''}: ${type.type};`); + fullList.push(`${padding}"${type.attributeName}"?: ${type.type};`); } return fullList; diff --git a/src/compiler/types/tests/generate-app-types.spec.ts b/src/compiler/types/tests/generate-app-types.spec.ts index 813fb5c7298..71a66c2c22d 100644 --- a/src/compiler/types/tests/generate-app-types.spec.ts +++ b/src/compiler/types/tests/generate-app-types.spec.ts @@ -847,6 +847,10 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -869,6 +873,10 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; @@ -949,7 +957,15 @@ export namespace Components { */ interface MyComponent { "email": SecondUserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: SecondUserImplementedPropType; "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -972,7 +988,15 @@ declare namespace LocalJSX { */ interface MyComponent { "email"?: SecondUserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: SecondUserImplementedPropType; "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; @@ -1063,12 +1087,20 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "fullName": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -1101,12 +1133,20 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "fullName"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; @@ -1207,12 +1247,20 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "newName": UserImplementedPropType1; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType1; } } declare global { @@ -1245,12 +1293,20 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "newName"?: UserImplementedPropType1; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType1; } interface IntrinsicElements { "my-component": MyComponent; @@ -1351,12 +1407,20 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "name": UserImplementedPropType1; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType1; } } declare global { @@ -1389,12 +1453,20 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } /** * docs */ interface MyNewComponent { "name"?: UserImplementedPropType1; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType1; } interface IntrinsicElements { "my-component": MyComponent; @@ -1472,6 +1544,10 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } export interface MyComponentCustomEvent extends CustomEvent { @@ -1509,6 +1585,10 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; "onMyEvent"?: (event: MyComponentCustomEvent) => void; } interface IntrinsicElements { @@ -1590,6 +1670,10 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -1612,6 +1696,10 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; @@ -1682,6 +1770,10 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -1704,6 +1796,10 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; @@ -1762,6 +1858,8 @@ declare module "@stencil/core" { await generateAppTypes(config, compilerCtx, buildCtx, 'src'); + console.log(mockWriteFile.mock.calls[0][1]); + expect(mockWriteFile).toHaveBeenCalledWith( '/components.d.ts', `/* eslint-disable */ @@ -1781,6 +1879,10 @@ export namespace Components { */ interface MyComponent { "name": UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } } declare global { @@ -1803,6 +1905,10 @@ declare namespace LocalJSX { */ interface MyComponent { "name"?: UserImplementedPropType; + /** + * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. + */ + "my-cmp"?: UserImplementedPropType; } interface IntrinsicElements { "my-component": MyComponent; diff --git a/test/end-to-end/src/components.d.ts b/test/end-to-end/src/components.d.ts index 681eeaa2a33..9a0e74ff143 100644 --- a/test/end-to-end/src/components.d.ts +++ b/test/end-to-end/src/components.d.ts @@ -43,7 +43,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "initial-counter": number; + "initial-counter"?: number; } interface CmpServerVsClient { } @@ -108,7 +108,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "some-prop": number; + "some-prop"?: number; } interface MyCmp { /** @@ -120,7 +120,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "bar-prop": string; + "bar-prop"?: string; /** * foo prop */ @@ -128,7 +128,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "foo-prop": string; + "foo-prop"?: string; /** * Mode */ @@ -144,7 +144,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "bar-prop": string; + "bar-prop"?: string; /** * foo prop */ @@ -152,7 +152,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "foo-prop": string; + "foo-prop"?: string; /** * Mode */ @@ -186,12 +186,12 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "full-name": string; + "full-name"?: string; "lastName": string; /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "last-name": string; + "last-name"?: string; /** * Mode */ @@ -202,17 +202,17 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "basic-prop": string; + "basic-prop"?: string; "decoratedGetterSetterProp": number; /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "decorated-getter-setter-prop": number; + "decorated-getter-setter-prop"?: number; "decoratedProp": number; /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "decorated-prop": number; + "decorated-prop"?: number; } interface ScopedCarDetail { "car": CarData; diff --git a/test/wdio/src/components.d.ts b/test/wdio/src/components.d.ts index bf58ad21c89..496b27ce901 100644 --- a/test/wdio/src/components.d.ts +++ b/test/wdio/src/components.d.ts @@ -23,7 +23,7 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "unique-id": string; + "unique-id"?: string; } interface CmpScopedA { } diff --git a/test/wdio/ts-target-props/components.d.ts b/test/wdio/ts-target-props/components.d.ts index 25c0a828000..6aec8ac94e8 100644 --- a/test/wdio/ts-target-props/components.d.ts +++ b/test/wdio/ts-target-props/components.d.ts @@ -11,17 +11,17 @@ export namespace Components { /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "basic-prop": string; + "basic-prop"?: string; "decoratedGetterSetterProp": number; /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "decorated-getter-setter-prop": number; + "decorated-getter-setter-prop"?: number; "decoratedProp": number; /** * @deprecated use camelCase instead. Support for dash-casing will be removed in Stencil v5. */ - "decorated-prop": number; + "decorated-prop"?: number; } } declare global {