diff --git a/src/core/resolvers/antdv.ts b/src/core/resolvers/antdv.ts index e3386628..239861c3 100644 --- a/src/core/resolvers/antdv.ts +++ b/src/core/resolvers/antdv.ts @@ -85,11 +85,14 @@ const matchComponents: IMatcher[] = [ pattern: /^Layout/, styleDir: 'layout', }, + { + pattern: /^Message/, + styleDir: 'message', + }, { pattern: /^Menu|^SubMenu/, styleDir: 'menu', }, - { pattern: /^Table/, styleDir: 'table', @@ -237,7 +240,7 @@ function getSideEffects(compName: string, options: AntDesignVueResolverOptions): return `${packageName}/${lib}/${styleDir}/style/css` } } -const primitiveNames = ['Affix', 'Anchor', 'AnchorLink', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider'] +const primitiveNames = ['Affix', 'Anchor', 'AnchorLink', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'Message'] const prefix = 'A' let antdvNames: Set diff --git a/src/core/transforms/component.ts b/src/core/transforms/component.ts index 9cd36b32..a92e54e9 100644 --- a/src/core/transforms/component.ts +++ b/src/core/transforms/component.ts @@ -1,6 +1,7 @@ +import { toArray } from '@antfu/utils' import Debug from 'debug' import type MagicString from 'magic-string' -import { pascalCase, stringifyComponentImport } from '../utils' +import { pascalCase, removeDuplicatesPrepend, stringifyComponentImport, stringifyImport } from '../utils' import type { Context } from '../context' import type { ResolveResult } from '../transformer' import type { SupportedTransformer } from '../..' @@ -45,9 +46,19 @@ const resolveVue3 = (code: string, s: MagicString) => { return results } +const componentUi = [{ + prefix: 'A', + name: 'ant-design-vue', +}, { + prefix: '', + name: 'element-plus', +}, { + prefix: '', + name: 'vant', +}] + export default async function transformComponent(code: string, transformer: SupportedTransformer, s: MagicString, ctx: Context, sfcPath: string) { let no = 0 - const results = transformer === 'vue2' ? resolveVue2(code, s) : resolveVue3(code, s) for (const { rawName, replace } of results) { @@ -57,11 +68,40 @@ export default async function transformComponent(code: string, transformer: Supp const component = await ctx.findComponent(name, 'component', [sfcPath]) if (component) { const varName = `__unplugin_components_${no}` - s.prepend(`${stringifyComponentImport({ ...component, as: varName }, ctx)};\n`) + removeDuplicatesPrepend(`${stringifyComponentImport({ ...component, as: varName }, ctx)}`, s) no += 1 replace(varName) } } + // Prevent templates and imports from being duplicated + const onlyStyle = onlyInjectStyle(code).filter(item => !results.some(({ rawName }) => rawName === item)) + for (const rawName of onlyStyle) { + const name = pascalCase(rawName) + ctx.updateUsageMap(sfcPath, [name]) + const component = await ctx.findComponent(name, 'component', [sfcPath]) + if (!component || !component.sideEffects) + continue + const sideEffects = toArray(component.sideEffects) + + removeDuplicatesPrepend(`${sideEffects.map(stringifyImport).join(';')}`, s) + } + debug(`^ (${no})`) } + +function onlyInjectStyle(code: string) { + const results: string[] = [] + for (const ui of componentUi) { + const { name, prefix } = ui + const ulReg = new RegExp(`import {(.*)} from ['"]${name}["']`) + const matcher = code.match(ulReg) + if (!matcher) + continue + results.push(...matcher[1].split(',').map((i) => { + i = i.trim() + return `${prefix}${i[0].toUpperCase() + i.slice(1)}` + })) + } + return results +} diff --git a/src/core/transforms/directive/index.ts b/src/core/transforms/directive/index.ts index 30c07d29..38ff2b20 100644 --- a/src/core/transforms/directive/index.ts +++ b/src/core/transforms/directive/index.ts @@ -1,6 +1,6 @@ import Debug from 'debug' import type MagicString from 'magic-string' -import { pascalCase, stringifyComponentImport } from '../../utils' +import { pascalCase, removeDuplicatesPrepend, stringifyComponentImport } from '../../utils' import type { Context } from '../../context' import type { SupportedTransformer } from '../../..' import { DIRECTIVE_IMPORT_PREFIX } from '../../constants' @@ -23,7 +23,7 @@ export default async function transformDirective(code: string, transformer: Supp continue const varName = `__unplugin_directives_${no}` - s.prepend(`${stringifyComponentImport({ ...directive, as: varName }, ctx)};\n`) + removeDuplicatesPrepend(`${stringifyComponentImport({ ...directive, as: varName }, ctx)}`, s) no += 1 replace(varName) } diff --git a/src/core/utils.ts b/src/core/utils.ts index 87f69866..f9e6b652 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -6,6 +6,7 @@ import { getPackageInfo, isPackageExists, } from 'local-pkg' +import type MagicString from 'magic-string' import type { ComponentInfo, ImportInfo, ImportInfoLegacy, Options, ResolvedOptions } from '../types' import type { Context } from './context' import { DISABLE_COMMENT } from './constants' @@ -225,3 +226,10 @@ export function resolveImportPath(importName: string): string | undefined { preserveSymlinks: false, }) } + +export function removeDuplicatesPrepend(content: string, s: MagicString) { + const r = !(s as any).intro + ? `${content};` + : `${content.split(';').filter(item => !(s as any).intro.includes(item)).join(';')};\n` + return s.prepend(r) +} diff --git a/test/__snapshots__/dts.test.ts.snap b/test/__snapshots__/dts.test.ts.snap index 99dc4c5f..8f35d023 100644 --- a/test/__snapshots__/dts.test.ts.snap +++ b/test/__snapshots__/dts.test.ts.snap @@ -1,4 +1,4 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`dts > components only 1`] = ` "/* eslint-disable */ diff --git a/test/__snapshots__/search.test.ts.snap b/test/__snapshots__/search.test.ts.snap index 27284a11..e635cc73 100644 --- a/test/__snapshots__/search.test.ts.snap +++ b/test/__snapshots__/search.test.ts.snap @@ -1,4 +1,4 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`search > should with namespace & collapse 1`] = ` [ diff --git a/test/__snapshots__/stringifyComponentImport.test.ts.snap b/test/__snapshots__/stringifyComponentImport.test.ts.snap index 002db3de..a6403cb5 100644 --- a/test/__snapshots__/stringifyComponentImport.test.ts.snap +++ b/test/__snapshots__/stringifyComponentImport.test.ts.snap @@ -1,4 +1,4 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`stringifyComponentImport > importName 1`] = `"import { a as Test } from 'test'"`; diff --git a/test/__snapshots__/transform.test.ts.snap b/test/__snapshots__/transform.test.ts.snap index 21f607f4..92147690 100644 --- a/test/__snapshots__/transform.test.ts.snap +++ b/test/__snapshots__/transform.test.ts.snap @@ -1,10 +1,9 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Component and directive as same name > vue2 transform should work 1`] = ` { "code": "/* unplugin-vue-components disabled */import __unplugin_directives_0 from 'test/directive/Loading'; import __unplugin_components_0 from 'test/component/Loading'; - var render = function () { this.$options.directives[\\"loading\\"] = __unplugin_directives_0; var _vm = this @@ -27,7 +26,6 @@ exports[`Component and directive as same name > vue2.7 transform should work 1`] { "code": "/* unplugin-vue-components disabled */import __unplugin_directives_0 from 'test/directive/Loading'; import __unplugin_components_0 from 'test/component/Div'; - import { defineComponent as _defineComponent } from \\"vue\\"; const _sfc_main = /* @__PURE__ */ _defineComponent({ __name: \\"App\\", @@ -48,7 +46,6 @@ exports[`Component and directive as same name > vue3 transform should work 1`] = { "code": "/* unplugin-vue-components disabled */import __unplugin_directives_0 from 'test/directive/ElInfiniteScroll'; import __unplugin_components_0 from 'test/component/ElInfiniteScroll'; - const render = (_ctx, _cache) => { const _component_el_infinite_scroll = __unplugin_components_0 const _directive_el_infinite_scroll = __unplugin_directives_0 @@ -67,7 +64,6 @@ exports[`transform > vue2 transform should work 1`] = ` { "code": "/* unplugin-vue-components disabled */import __unplugin_directives_0 from 'test/directive/Loading'; import __unplugin_components_0 from 'test/component/TestComp'; - var render = function () { this.$options.directives[\\"loading\\"] = __unplugin_directives_0; var _vm = this @@ -89,7 +85,6 @@ this.$options.directives[\\"loading\\"] = __unplugin_directives_0; exports[`transform > vue2 transform with jsx should work 1`] = ` { "code": "/* unplugin-vue-components disabled */import __unplugin_components_0 from 'test/component/TestComp'; - export default { render(){ return h(__unplugin_components_0, { @@ -107,7 +102,6 @@ exports[`transform > vue3 transform should work 1`] = ` { "code": "/* unplugin-vue-components disabled */import __unplugin_directives_0 from 'test/directive/Loading'; import __unplugin_components_0 from 'test/component/TestComp'; - const render = (_ctx, _cache) => { const _component_test_comp = __unplugin_components_0 const _directive_loading = __unplugin_directives_0 diff --git a/test/resolvers/__snapshots__/ant-design-vue.test.ts.snap b/test/resolvers/__snapshots__/ant-design-vue.test.ts.snap new file mode 100644 index 00000000..35ce27f0 --- /dev/null +++ b/test/resolvers/__snapshots__/ant-design-vue.test.ts.snap @@ -0,0 +1,105 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Ant Design Vue Resolver > components and directives should be transformed 1`] = ` +{ + "code": "/* unplugin-vue-components disabled */import 'ant-design-vue/es/message/style/css'; +import { Button as __unplugin_components_0 } from 'ant-design-vue/es';import 'ant-design-vue/es/button/style/css'; + import { defineComponent as _defineComponent } from \\"vue\\"; +' + + 'import { message } from \\"ant-design-vue\\"; +' + + 'import { ElMessage } from \\"element-plus\\"; +' + + 'const _sfc_main = /* @__PURE__ */ _defineComponent({ +' + + ' __name: \\"App\\", +' + + ' setup(__props, { expose }) { +' + + ' expose(); +' + + ' const test1 = () => { +' + + ' ElMessage.success(\\"message\\"); +' + + ' }; +' + + ' const handleClick = () => { +' + + ' message.success(\\"\\\\u6D4B\\\\u8BD5 jsx \\\\u4F7F\\\\u7528 a-button \\\\u548C mesage\\"); +' + + ' }; +' + + ' const __returned__ = { test1, handleClick }; +' + + ' Object.defineProperty(__returned__, \\"__isScriptSetup\\", { enumerable: false, value: true }); +' + + ' return __returned__; +' + + ' } +' + + '}); +' + + 'import { createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\"; +' + + 'const _withScopeId = (n) => (_pushScopeId(\\"data-v-7a7a37b1\\"), n = n(), _popScopeId(), n); +' + + 'const _hoisted_1 = { class: \\"block\\" }; +' + + 'function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { +' + + ' const _component_a_button = __unplugin_components_0; +' + + ' return _openBlock(), _createElementBlock(\\"div\\", _hoisted_1, [ +' + + ' _createVNode(_component_a_button, { onClick: $setup.handleClick }, { +' + + ' default: _withCtx(() => [ +' + + ' _createTextVNode(\\" \\\\u70B9\\\\u6211 \\") +' + + ' ]), +' + + ' _: 1 +' + + ' /* STABLE */ +' + + ' }) +' + + ' ]); +' + + '} +' + + 'import \\"/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue?vue&type=style&index=0&scoped=7a7a37b1&lang.css\\"; +' + + '_sfc_main.__hmrId = \\"7a7a37b1\\"; +' + + 'typeof __VUE_HMR_RUNTIME__ !== \\"undefined\\" && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main); +' + + 'import.meta.hot.accept((mod) => { +' + + ' if (!mod) +' + + ' return; +' + + ' const { default: updated, _rerender_only } = mod; +' + + ' if (_rerender_only) { +' + + ' __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render); +' + + ' } else { +' + + ' __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated); +' + + ' } +' + + '}); +' + + 'import _export_sfc from \\"\\\\0plugin-vue:export-helper\\"; +' + + 'export default /* @__PURE__ */ _export_sfc(_sfc_main, [[\\"render\\", _sfc_render], [\\"__scopeId\\", \\"data-v-7a7a37b1\\"], [\\"__file\\", \\"/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue\\"]]); + +", +} +`; diff --git a/test/resolvers/__snapshots__/element-plus.test.ts.snap b/test/resolvers/__snapshots__/element-plus.test.ts.snap index 9b448706..9b035d83 100644 --- a/test/resolvers/__snapshots__/element-plus.test.ts.snap +++ b/test/resolvers/__snapshots__/element-plus.test.ts.snap @@ -1,11 +1,103 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Element Plus Resolver > auto load dynamically created component styles 1`] = ` +{ + "code": "/* unplugin-vue-components disabled */import { ElLoadingDirective as __unplugin_directives_0 } from 'element-plus/es';import 'element-plus/es/components/loading/style/css'; +import 'element-plus/es/components/message-box/style/css'; +import 'element-plus/es/components/message/style/css'; +import { ElConfigProvider as __unplugin_components_1 } from 'element-plus/es';import 'element-plus/es/components/config-provider/style/css'; +import { ElButton as __unplugin_components_0 } from 'element-plus/es';import 'element-plus/es/components/base/style/css';import 'element-plus/es/components/button/style/css'; + import { ref } from 'vue' + import { ElMessage, ElMessageBox } from 'element-plus' + + const _sfc_main = { + __name: 'App', + setup(__props, { expose }) { + expose(); + + const loading = ref(true) + const test1 = (_) => { + ElMessage.success('message') + } + const test2 = (_) => { + ElMessageBox.confirm( + 'ElMessageBox', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + center: true, + }, + ) + } + + const __returned__ = { loading, test1, test2, ref, get ElMessage() { return ElMessage }, get ElMessageBox() { return ElMessageBox } } + Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true }) + return __returned__ + } + + } + import { createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, resolveDirective as _resolveDirective, openBlock as _openBlock, createBlock as _createBlock, withDirectives as _withDirectives, Fragment as _Fragment, createElementBlock as _createElementBlock } from \\"vue\\" + + function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { + const _component_el_button = __unplugin_components_0 + const _component_el_config_provider = __unplugin_components_1 + const _directive_loading = __unplugin_directives_0 + + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createVNode(_component_el_config_provider, { namespace: \\"ep\\" }, { + default: _withCtx(() => [ + _createVNode(_component_el_button, { onClick: $setup.test1 }, { + default: _withCtx(() => [ + _createTextVNode(\\" el-message \\") + ]), + _: 1 /* STABLE */ + }), + _createVNode(_component_el_button, { onClick: $setup.test2 }, { + default: _withCtx(() => [ + _createTextVNode(\\" el-messageBox \\") + ]), + _: 1 /* STABLE */ + }), + _withDirectives((_openBlock(), _createBlock(_component_el_button, null, { + default: _withCtx(() => [ + _createTextVNode(\\" loading... \\") + ]), + _: 1 /* STABLE */ + })), [ + [_directive_loading, $setup.loading] + ]) + ]), + _: 1 /* STABLE */ + }), + _createVNode($setup[\\"ElMessage\\"]) + ], 64 /* STABLE_FRAGMENT */)) + } + + + _sfc_main.__hmrId = \\"7a7a37b1\\" + typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main) + import.meta.hot.accept(mod => { + if (!mod) return + const { default: updated, _rerender_only } = mod + if (_rerender_only) { + __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render) + } else { + __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated) + } + }) + import _export_sfc from 'plugin-vue:export-helper' + export default /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render],['__file',\\"/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue\\"]]) +", +} +`; exports[`Element Plus Resolver > components and directives should be transformed 1`] = ` { - "code": "/* unplugin-vue-components disabled */import { ElLoadingDirective as __unplugin_directives_0 } from 'element-plus/es';import 'element-plus/es/components/base/style/css';import 'element-plus/es/components/loading/style/css'; + "code": "/* unplugin-vue-components disabled */import { ElLoadingDirective as __unplugin_directives_0 } from 'element-plus/es';import 'element-plus/es/components/loading/style/css'; import { Apple as __unplugin_components_1 } from '@element-plus/icons-vue'; import { ElButton as __unplugin_components_0 } from 'element-plus/es';import 'element-plus/es/components/base/style/css';import 'element-plus/es/components/button/style/css'; - (_ctx, _cache) => { const _component_el_button = __unplugin_components_0 const _component_el_icon_apple = __unplugin_components_1 diff --git a/test/resolvers/ant-design-vue.test.ts b/test/resolvers/ant-design-vue.test.ts new file mode 100644 index 00000000..95299b54 --- /dev/null +++ b/test/resolvers/ant-design-vue.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from 'vitest' +import { AntDesignVueResolver } from '../../src/resolvers' +import { Context } from '../../src/core/context' + +describe('Ant Design Vue Resolver', () => { + it('components and directives should be transformed', async () => { + const code = ` + import { defineComponent as _defineComponent } from "vue";\n' + + 'import { message } from "ant-design-vue";\n' + + 'import { ElMessage } from "element-plus";\n' + + 'const _sfc_main = /* @__PURE__ */ _defineComponent({\n' + + ' __name: "App",\n' + + ' setup(__props, { expose }) {\n' + + ' expose();\n' + + ' const test1 = () => {\n' + + ' ElMessage.success("message");\n' + + ' };\n' + + ' const handleClick = () => {\n' + + ' message.success("\\u6D4B\\u8BD5 jsx \\u4F7F\\u7528 a-button \\u548C mesage");\n' + + ' };\n' + + ' const __returned__ = { test1, handleClick };\n' + + ' Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });\n' + + ' return __returned__;\n' + + ' }\n' + + '});\n' + + 'import { createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from "vue";\n' + + 'const _withScopeId = (n) => (_pushScopeId("data-v-7a7a37b1"), n = n(), _popScopeId(), n);\n' + + 'const _hoisted_1 = { class: "block" };\n' + + 'function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {\n' + + ' const _component_a_button = _resolveComponent("a-button");\n' + + ' return _openBlock(), _createElementBlock("div", _hoisted_1, [\n' + + ' _createVNode(_component_a_button, { onClick: $setup.handleClick }, {\n' + + ' default: _withCtx(() => [\n' + + ' _createTextVNode(" \\u70B9\\u6211 ")\n' + + ' ]),\n' + + ' _: 1\n' + + ' /* STABLE */\n' + + ' })\n' + + ' ]);\n' + + '}\n' + + 'import "/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue?vue&type=style&index=0&scoped=7a7a37b1&lang.css";\n' + + '_sfc_main.__hmrId = "7a7a37b1";\n' + + 'typeof __VUE_HMR_RUNTIME__ !== "undefined" && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main);\n' + + 'import.meta.hot.accept((mod) => {\n' + + ' if (!mod)\n' + + ' return;\n' + + ' const { default: updated, _rerender_only } = mod;\n' + + ' if (_rerender_only) {\n' + + ' __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render);\n' + + ' } else {\n' + + ' __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated);\n' + + ' }\n' + + '});\n' + + 'import _export_sfc from "\\0plugin-vue:export-helper";\n' + + 'export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-7a7a37b1"], ["__file", "/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue"]]);\n +` + + const ctx = new Context({ + resolvers: [AntDesignVueResolver({})], + transformer: 'vue3', + directives: true, + }) + ctx.sourcemap = false + expect(await ctx.transform(code, '')).toMatchSnapshot() + }) +}) diff --git a/test/resolvers/element-plus.test.ts b/test/resolvers/element-plus.test.ts index a8ce6032..22476f08 100644 --- a/test/resolvers/element-plus.test.ts +++ b/test/resolvers/element-plus.test.ts @@ -28,4 +28,99 @@ describe('Element Plus Resolver', () => { ctx.sourcemap = false expect(await ctx.transform(code, '')).toMatchSnapshot() }) + + it('auto load dynamically created component styles', async () => { + const code = ` + import { ref } from 'vue' + import { ElMessage, ElMessageBox } from 'element-plus' + + const _sfc_main = { + __name: 'App', + setup(__props, { expose }) { + expose(); + + const loading = ref(true) + const test1 = (_) => { + ElMessage.success('message') + } + const test2 = (_) => { + ElMessageBox.confirm( + 'ElMessageBox', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + center: true, + }, + ) + } + + const __returned__ = { loading, test1, test2, ref, get ElMessage() { return ElMessage }, get ElMessageBox() { return ElMessageBox } } + Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true }) + return __returned__ + } + + } + import { createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, resolveDirective as _resolveDirective, openBlock as _openBlock, createBlock as _createBlock, withDirectives as _withDirectives, Fragment as _Fragment, createElementBlock as _createElementBlock } from "vue" + + function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { + const _component_el_button = _resolveComponent("el-button") + const _component_el_config_provider = _resolveComponent("el-config-provider") + const _directive_loading = _resolveDirective("loading") + + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createVNode(_component_el_config_provider, { namespace: "ep" }, { + default: _withCtx(() => [ + _createVNode(_component_el_button, { onClick: $setup.test1 }, { + default: _withCtx(() => [ + _createTextVNode(" el-message ") + ]), + _: 1 /* STABLE */ + }), + _createVNode(_component_el_button, { onClick: $setup.test2 }, { + default: _withCtx(() => [ + _createTextVNode(" el-messageBox ") + ]), + _: 1 /* STABLE */ + }), + _withDirectives((_openBlock(), _createBlock(_component_el_button, null, { + default: _withCtx(() => [ + _createTextVNode(" loading... ") + ]), + _: 1 /* STABLE */ + })), [ + [_directive_loading, $setup.loading] + ]) + ]), + _: 1 /* STABLE */ + }), + _createVNode($setup["ElMessage"]) + ], 64 /* STABLE_FRAGMENT */)) + } + + + _sfc_main.__hmrId = "7a7a37b1" + typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main) + import.meta.hot.accept(mod => { + if (!mod) return + const { default: updated, _rerender_only } = mod + if (_rerender_only) { + __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render) + } else { + __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated) + } + }) + import _export_sfc from 'plugin-vue:export-helper' + export default /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render],['__file',"/Users/hejian/Documents/Github/unplugin-vue-components/examples/vite-vue3/src/App.vue"]]) +` + + const ctx = new Context({ + resolvers: [ElementPlusResolver({})], + transformer: 'vue3', + directives: true, + }) + ctx.sourcemap = false + expect(await ctx.transform(code, '')).toMatchSnapshot() + }) })