From 7306078d2a18e075c733e5acda9b0525fc476854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:45:06 +0800 Subject: [PATCH 01/11] fix(custom-element): Correctly handle number type props in prod --- packages/runtime-dom/src/apiCustomElement.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 5662b0b535b..64458814254 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -255,7 +255,7 @@ export class VueElement extends BaseClass { if (props && !isArray(props)) { for (const key in props) { const opt = props[key] - if (opt === Number || (opt && opt.type === Number)) { + if (opt === Number || (opt && (opt.type === Number || typeof opt.default === 'number'))) { if (key in this._props) { this._props[key] = toNumber(this._props[key]) } @@ -360,6 +360,7 @@ export class VueElement extends BaseClass { } private _createVNode(): VNode { + console.log('####', this._props) const vnode = createVNode(this._def, extend({}, this._props)) if (!this._instance) { vnode.ce = instance => { From 981852fa9b0796c1536f9c8e5c82f83deae99335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Thu, 17 Aug 2023 00:06:52 +0800 Subject: [PATCH 02/11] fix(custom-element): added unit test --- .../__tests__/customElement.spec.ts | 20 +++++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 6ccf02c4fa6..a5dbad834c0 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -10,6 +10,7 @@ import { renderSlot, VueElement } from '../src' +import { expect } from 'vitest' describe('defineCustomElement', () => { const container = document.createElement('div') @@ -283,6 +284,25 @@ describe('defineCustomElement', () => { expect(el.maxAge).toBe(50) expect(el.shadowRoot.innerHTML).toBe('max age: 50/type: number') }) + + // #8987 + test('Correctly handle number type props in prod', () => { + const E = defineCustomElement({ + props: { + foo: { + default: 5 + } + }, + render() { + // @ts-ignore + return `foo: ${typeof this.foo}` + } + }) + customElements.define('my-element-number-property-prod', E) + container.innerHTML = `` + const e = container.childNodes[0] as VueElement + expect(e.shadowRoot!.innerHTML).toBe('foo: number') + }) }) describe('attrs', () => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 64458814254..67c8fb0be95 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -255,7 +255,10 @@ export class VueElement extends BaseClass { if (props && !isArray(props)) { for (const key in props) { const opt = props[key] - if (opt === Number || (opt && (opt.type === Number || typeof opt.default === 'number'))) { + if ( + opt === Number || + (opt && (opt.type === Number || typeof opt.default === 'number')) + ) { if (key in this._props) { this._props[key] = toNumber(this._props[key]) } From df02ef96aa4c34ebf1f1a424c6a976ad3b0c4ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:04:00 +0800 Subject: [PATCH 03/11] chore: remove console --- packages/runtime-dom/src/apiCustomElement.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 67c8fb0be95..d5032beaea3 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -363,7 +363,6 @@ export class VueElement extends BaseClass { } private _createVNode(): VNode { - console.log('####', this._props) const vnode = createVNode(this._def, extend({}, this._props)) if (!this._instance) { vnode.ce = instance => { From bd3b0f50e9ff373bfaf9393493c158fd5625cbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:04:44 +0800 Subject: [PATCH 04/11] chore: remove expect --- packages/runtime-dom/__tests__/customElement.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index a5dbad834c0..b07868af705 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -10,7 +10,6 @@ import { renderSlot, VueElement } from '../src' -import { expect } from 'vitest' describe('defineCustomElement', () => { const container = document.createElement('div') From ad7dd235641f0a1c61cfdbcdf7e89308e73ff06d Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Fri, 18 Aug 2023 18:11:53 +0800 Subject: [PATCH 05/11] chore: updated complier --- packages/compiler-sfc/src/script/defineProps.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/compiler-sfc/src/script/defineProps.ts b/packages/compiler-sfc/src/script/defineProps.ts index 5004e314da1..8cc34174eab 100644 --- a/packages/compiler-sfc/src/script/defineProps.ts +++ b/packages/compiler-sfc/src/script/defineProps.ts @@ -274,8 +274,16 @@ function genRuntimePropFromType( defaultString ])} }` } else { - // production: checks are useless - return `${finalKey}: ${defaultString ? `{ ${defaultString} }` : `{}`}` + if(defaultString){ + return `${finalKey}: ${`{ ${defaultString} }`}` + }else{ + if(/\.ce\.vue$/.test(ctx.filename)){ + return `${finalKey}: {type: ${toRuntimeTypeString(type)}}` + }else{ + // production: checks are useless + return `${finalKey}: {}` + } + } } } From 057886a4f22b24e326bf6dc6bbdbb57f33b15e3c Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Mon, 21 Aug 2023 08:48:01 +0800 Subject: [PATCH 06/11] chore: format code --- packages/compiler-sfc/src/script/defineProps.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/compiler-sfc/src/script/defineProps.ts b/packages/compiler-sfc/src/script/defineProps.ts index 8cc34174eab..aa4663f900e 100644 --- a/packages/compiler-sfc/src/script/defineProps.ts +++ b/packages/compiler-sfc/src/script/defineProps.ts @@ -274,12 +274,12 @@ function genRuntimePropFromType( defaultString ])} }` } else { - if(defaultString){ + if (defaultString) { return `${finalKey}: ${`{ ${defaultString} }`}` - }else{ - if(/\.ce\.vue$/.test(ctx.filename)){ + } else { + if (/\.ce\.vue$/.test(ctx.filename)) { return `${finalKey}: {type: ${toRuntimeTypeString(type)}}` - }else{ + } else { // production: checks are useless return `${finalKey}: {}` } From b0d74173d95dd85b248e6f44e114877e722d33b5 Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Mon, 21 Aug 2023 09:09:21 +0800 Subject: [PATCH 07/11] chore: added isCE option to compiler sfc ctx --- packages/compiler-sfc/src/script/context.ts | 3 +++ packages/compiler-sfc/src/script/defineProps.ts | 17 ++++++++++------- packages/runtime-dom/src/apiCustomElement.ts | 5 +---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/compiler-sfc/src/script/context.ts b/packages/compiler-sfc/src/script/context.ts index 5fe09d28a42..e76e5270661 100644 --- a/packages/compiler-sfc/src/script/context.ts +++ b/packages/compiler-sfc/src/script/context.ts @@ -12,6 +12,7 @@ import { TypeScope } from './resolveType' export class ScriptCompileContext { isJS: boolean isTS: boolean + isCE: boolean scriptAst: Program | null scriptSetupAst: Program | null @@ -95,6 +96,8 @@ export class ScriptCompileContext { scriptSetupLang === 'ts' || scriptSetupLang === 'tsx' + this.isCE = /\.ce\.vue$/.test(this.descriptor.filename) + // resolve parser plugins const plugins: ParserPlugin[] = resolveParserPlugins( (scriptLang || scriptSetupLang)!, diff --git a/packages/compiler-sfc/src/script/defineProps.ts b/packages/compiler-sfc/src/script/defineProps.ts index aa4663f900e..c3c47498f70 100644 --- a/packages/compiler-sfc/src/script/defineProps.ts +++ b/packages/compiler-sfc/src/script/defineProps.ts @@ -274,16 +274,19 @@ function genRuntimePropFromType( defaultString ])} }` } else { - if (defaultString) { - return `${finalKey}: ${`{ ${defaultString} }`}` - } else { - if (/\.ce\.vue$/.test(ctx.filename)) { - return `${finalKey}: {type: ${toRuntimeTypeString(type)}}` + // #8989 for custom element, should keep the type + if (ctx.isCE) { + if (defaultString) { + return `${finalKey}: ${`{ ${defaultString}, type: ${toRuntimeTypeString( + type + )} }`}` } else { - // production: checks are useless - return `${finalKey}: {}` + return `${finalKey}: {type: ${toRuntimeTypeString(type)}}` } } + + // production: checks are useless + return `${finalKey}: ${defaultString ? `{ ${defaultString} }` : `{}`}` } } diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index d5032beaea3..5662b0b535b 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -255,10 +255,7 @@ export class VueElement extends BaseClass { if (props && !isArray(props)) { for (const key in props) { const opt = props[key] - if ( - opt === Number || - (opt && (opt.type === Number || typeof opt.default === 'number')) - ) { + if (opt === Number || (opt && opt.type === Number)) { if (key in this._props) { this._props[key] = toNumber(this._props[key]) } From 8f1da9a987f1296abf093771f7e3c82d5cc3bdbc Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Mon, 21 Aug 2023 09:38:00 +0800 Subject: [PATCH 08/11] chore: updated unit test --- .../__snapshots__/defineProps.spec.ts.snap | 60 +++++++++++++++++++ .../compileScript/defineProps.spec.ts | 31 ++++++++++ .../__tests__/customElement.spec.ts | 19 ------ 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineProps.spec.ts.snap b/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineProps.spec.ts.snap index 30e00e518b2..8034bc6a6e1 100644 --- a/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineProps.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineProps.spec.ts.snap @@ -18,6 +18,66 @@ return { props, bar } }" `; +exports[`defineProps > custom element retains the props type & default value & production mode 1`] = ` +"import { defineComponent as _defineComponent } from 'vue' +interface Props { + foo?: number; + } + +export default /*#__PURE__*/_defineComponent({ + __name: 'app.ce', + props: { + foo: { default: 5.5, type: Number } + }, + setup(__props: any, { expose: __expose }) { + __expose(); + + const props = __props; + +return { props } +} + +})" +`; + +exports[`defineProps > custom element retains the props type & production mode 1`] = ` +"import { defineComponent as _defineComponent } from 'vue' + +export default /*#__PURE__*/_defineComponent({ + __name: 'app.ce', + props: { + foo: {type: Number} + }, + setup(__props: any, { expose: __expose }) { + __expose(); + + const props = __props + +return { props } +} + +})" +`; + +exports[`defineProps > custom element retains the props type w/ production mode 1`] = ` +"import { defineComponent as _defineComponent } from 'vue' + +export default /*#__PURE__*/_defineComponent({ + __name: 'app.ce', + props: { + foo: {type: Number} + }, + setup(__props: any, { expose: __expose }) { + __expose(); + + const props = __props + +return { props } +} + +})" +`; + exports[`defineProps > defineProps w/ runtime options 1`] = ` "import { defineComponent as _defineComponent } from 'vue' diff --git a/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts b/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts index 43f54b0aa1e..4fe4a9d6459 100644 --- a/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts @@ -608,4 +608,35 @@ const props = defineProps({ foo: String }) }).toThrow(`cannot accept both type and non-type arguments`) }) }) + + // #8989 + test('custom element retains the props type & production mode', () => { + const { content } = compile( + ``, + { isProd: true }, + { filename: 'app.ce.vue' } + ) + + expect(content).toMatch(`foo: {type: Number}`) + assertCode(content) + }) + + test('custom element retains the props type & default value & production mode', () => { + const { content } = compile( + ``, + { isProd: true }, + { filename: 'app.ce.vue' } + ) + expect(content).toMatch(`foo: { default: 5.5, type: Number }`) + assertCode(content) + }) }) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index b07868af705..6ccf02c4fa6 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -283,25 +283,6 @@ describe('defineCustomElement', () => { expect(el.maxAge).toBe(50) expect(el.shadowRoot.innerHTML).toBe('max age: 50/type: number') }) - - // #8987 - test('Correctly handle number type props in prod', () => { - const E = defineCustomElement({ - props: { - foo: { - default: 5 - } - }, - render() { - // @ts-ignore - return `foo: ${typeof this.foo}` - } - }) - customElements.define('my-element-number-property-prod', E) - container.innerHTML = `` - const e = container.childNodes[0] as VueElement - expect(e.shadowRoot!.innerHTML).toBe('foo: number') - }) }) describe('attrs', () => { From 4a651d1f3184037c7e5339229d5a2103f7dee2a9 Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Tue, 22 Aug 2023 10:14:30 +0800 Subject: [PATCH 09/11] chore: added customElement options in SFCScriptCompileOptions --- packages/compiler-sfc/src/compileScript.ts | 8 ++++++++ packages/compiler-sfc/src/script/context.ts | 22 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index cfcc607c72d..42ab45d1396 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -130,6 +130,14 @@ export interface SFCScriptCompileOptions { * using it, disable this and switch to the [Vue Macros implementation](https://vue-macros.sxzz.moe/features/reactivity-transform.html). */ reactivityTransform?: boolean + /** + * Transform Vue SFCs into custom elements. + * - `true`: all `*.vue` imports are converted into custom elements + * - `RegExp`: matched files are converted into custom elements + * + * @default /\.ce\.vue$/ + */ + customElement?: boolean | RegExp | RegExp[] } export interface ImportBinding { diff --git a/packages/compiler-sfc/src/script/context.ts b/packages/compiler-sfc/src/script/context.ts index e76e5270661..b776c490d37 100644 --- a/packages/compiler-sfc/src/script/context.ts +++ b/packages/compiler-sfc/src/script/context.ts @@ -1,6 +1,6 @@ import { CallExpression, Node, ObjectPattern, Program } from '@babel/types' import { SFCDescriptor } from '../parse' -import { generateCodeFrame } from '@vue/shared' +import { generateCodeFrame, isArray, isRegExp } from '@vue/shared' import { parse as babelParse, ParserPlugin } from '@babel/parser' import { ImportBinding, SFCScriptCompileOptions } from '../compileScript' import { PropsDestructureBindings } from './defineProps' @@ -12,7 +12,7 @@ import { TypeScope } from './resolveType' export class ScriptCompileContext { isJS: boolean isTS: boolean - isCE: boolean + isCE = false scriptAst: Program | null scriptSetupAst: Program | null @@ -96,7 +96,23 @@ export class ScriptCompileContext { scriptSetupLang === 'ts' || scriptSetupLang === 'tsx' - this.isCE = /\.ce\.vue$/.test(this.descriptor.filename) + const customElement = options.customElement + const filename = this.descriptor.filename + + let isCE = false + if (typeof customElement === 'boolean') { + isCE = customElement + } else if (isRegExp(customElement)) { + isCE = customElement.test(filename) + } else if (isArray(customElement)) { + isCE = customElement.some( + pattern => isRegExp(pattern) && pattern.test(filename) + ) + } else { + isCE = /\.ce\.vue$/.test(filename) + } + + this.isCE = isCE // resolve parser plugins const plugins: ParserPlugin[] = resolveParserPlugins( From dbfaf97251aba4d1f3f2f80c08871f02afdae0a7 Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Tue, 22 Aug 2023 14:48:19 +0800 Subject: [PATCH 10/11] chore: updated code --- .../compileScript/defineProps.spec.ts | 4 ++-- packages/compiler-sfc/src/compileScript.ts | 6 +---- packages/compiler-sfc/src/script/context.ts | 22 +++++-------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts b/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts index 4fe4a9d6459..457ff68b915 100644 --- a/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts @@ -615,7 +615,7 @@ const props = defineProps({ foo: String }) ``, - { isProd: true }, + { isProd: true, customElement: filename => /\.ce\.vue$/.test(filename) }, { filename: 'app.ce.vue' } ) @@ -633,7 +633,7 @@ const props = defineProps({ foo: String }) foo: 5.5, }); `, - { isProd: true }, + { isProd: true, customElement: filename => /\.ce\.vue$/.test(filename) }, { filename: 'app.ce.vue' } ) expect(content).toMatch(`foo: { default: 5.5, type: Number }`) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 42ab45d1396..b727c54a940 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -132,12 +132,8 @@ export interface SFCScriptCompileOptions { reactivityTransform?: boolean /** * Transform Vue SFCs into custom elements. - * - `true`: all `*.vue` imports are converted into custom elements - * - `RegExp`: matched files are converted into custom elements - * - * @default /\.ce\.vue$/ */ - customElement?: boolean | RegExp | RegExp[] + customElement?: boolean | ((filename: string) => boolean) } export interface ImportBinding { diff --git a/packages/compiler-sfc/src/script/context.ts b/packages/compiler-sfc/src/script/context.ts index b776c490d37..626262b86b6 100644 --- a/packages/compiler-sfc/src/script/context.ts +++ b/packages/compiler-sfc/src/script/context.ts @@ -1,6 +1,6 @@ import { CallExpression, Node, ObjectPattern, Program } from '@babel/types' import { SFCDescriptor } from '../parse' -import { generateCodeFrame, isArray, isRegExp } from '@vue/shared' +import { generateCodeFrame } from '@vue/shared' import { parse as babelParse, ParserPlugin } from '@babel/parser' import { ImportBinding, SFCScriptCompileOptions } from '../compileScript' import { PropsDestructureBindings } from './defineProps' @@ -98,22 +98,12 @@ export class ScriptCompileContext { const customElement = options.customElement const filename = this.descriptor.filename - - let isCE = false - if (typeof customElement === 'boolean') { - isCE = customElement - } else if (isRegExp(customElement)) { - isCE = customElement.test(filename) - } else if (isArray(customElement)) { - isCE = customElement.some( - pattern => isRegExp(pattern) && pattern.test(filename) - ) - } else { - isCE = /\.ce\.vue$/.test(filename) + if (customElement) { + this.isCE = + typeof customElement === 'boolean' + ? customElement + : customElement(filename) } - - this.isCE = isCE - // resolve parser plugins const plugins: ParserPlugin[] = resolveParserPlugins( (scriptLang || scriptSetupLang)!, From d73f608323547b8f33b6a10481e07b90687b6290 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 06:28:22 +0000 Subject: [PATCH 11/11] [autofix.ci] apply automated fixes --- packages/dts-test/tsx.test-d.tsx | 20 +++++--------------- packages/runtime-dom/src/jsx.ts | 8 +++++++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/dts-test/tsx.test-d.tsx b/packages/dts-test/tsx.test-d.tsx index 47555a03c0b..a15d16ac4d2 100644 --- a/packages/dts-test/tsx.test-d.tsx +++ b/packages/dts-test/tsx.test-d.tsx @@ -18,25 +18,15 @@ expectType( ) // #7955 -expectType( -
-) +expectType(
) -expectType( -
-) +expectType(
) -expectType( -
-) +expectType(
) -expectType( -
-) +expectType(
) -expectType( -
-) +expectType(
) // @ts-expect-error ;
diff --git a/packages/runtime-dom/src/jsx.ts b/packages/runtime-dom/src/jsx.ts index 3ab2de8b772..07645260eab 100644 --- a/packages/runtime-dom/src/jsx.ts +++ b/packages/runtime-dom/src/jsx.ts @@ -244,7 +244,13 @@ interface AriaAttributes { } // Vue's style normalization supports nested arrays -export type StyleValue = false | null | undefined | string | CSSProperties | Array +export type StyleValue = + | false + | null + | undefined + | string + | CSSProperties + | Array export interface HTMLAttributes extends AriaAttributes, EventHandlers { innerHTML?: string