From d74d364d62db8e48881af6b5a75ce4fb5f36cc35 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, 6 Dec 2023 15:16:16 +0800 Subject: [PATCH] fix(custom-element): correctly handle number type props in prod (#8989) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../__snapshots__/defineProps.spec.ts.snap | 60 +++++++++++++++++++ .../compileScript/defineProps.spec.ts | 31 ++++++++++ packages/compiler-sfc/src/compileScript.ts | 4 ++ packages/compiler-sfc/src/script/context.ts | 9 +++ .../compiler-sfc/src/script/defineProps.ts | 11 ++++ 5 files changed, 115 insertions(+) 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 38c4419b5d3..3fbd3a9b7b5 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 23d6a806f0d..7d46c4d2187 100644 --- a/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts @@ -710,4 +710,35 @@ const props = defineProps({ foo: String }) 'da-sh': BindingTypes.PROPS }) }) + + // #8989 + test('custom element retains the props type & production mode', () => { + const { content } = compile( + ``, + { isProd: true, customElement: filename => /\.ce\.vue$/.test(filename) }, + { 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, customElement: filename => /\.ce\.vue$/.test(filename) }, + { filename: 'app.ce.vue' } + ) + expect(content).toMatch(`foo: { default: 5.5, type: Number }`) + assertCode(content) + }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 9a05d3b327f..91f52707d68 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -130,6 +130,10 @@ 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. + */ + 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 b05b8d910ee..3dc5d3ff0cd 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 = false scriptAst: Program | null scriptSetupAst: Program | null @@ -95,6 +96,14 @@ export class ScriptCompileContext { scriptSetupLang === 'ts' || scriptSetupLang === 'tsx' + const customElement = options.customElement + const filename = this.descriptor.filename + if (customElement) { + this.isCE = + typeof customElement === 'boolean' + ? customElement + : customElement(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 0d35cdd776c..431f8d949a6 100644 --- a/packages/compiler-sfc/src/script/defineProps.ts +++ b/packages/compiler-sfc/src/script/defineProps.ts @@ -276,6 +276,17 @@ function genRuntimePropFromType( defaultString ])} }` } else { + // #8989 for custom element, should keep the type + if (ctx.isCE) { + if (defaultString) { + return `${finalKey}: ${`{ ${defaultString}, type: ${toRuntimeTypeString( + type + )} }`}` + } else { + return `${finalKey}: {type: ${toRuntimeTypeString(type)}}` + } + } + // production: checks are useless return `${finalKey}: ${defaultString ? `{ ${defaultString} }` : `{}`}` }