Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: script style auto import #615

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/core/resolvers/antdv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,14 @@ const matchComponents: IMatcher[] = [
pattern: /^Layout/,
styleDir: 'layout',
},
{
pattern: /^Message/,
styleDir: 'message',
},
{
pattern: /^Menu|^SubMenu/,
styleDir: 'menu',
},

{
pattern: /^Table/,
styleDir: 'table',
Expand Down Expand Up @@ -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<string>
Expand Down
46 changes: 43 additions & 3 deletions src/core/transforms/component.ts
Original file line number Diff line number Diff line change
@@ -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 '../..'
Expand Down Expand Up @@ -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) {
Expand All @@ -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
}
4 changes: 2 additions & 2 deletions src/core/transforms/directive/index.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
}
2 changes: 1 addition & 1 deletion test/__snapshots__/dts.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Vitest Snapshot v1
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`dts > components only 1`] = `
"/* eslint-disable */
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/search.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Vitest Snapshot v1
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`search > should with namespace & collapse 1`] = `
[
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/stringifyComponentImport.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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'"`;

Expand Down
8 changes: 1 addition & 7 deletions test/__snapshots__/transform.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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\\",
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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, {
Expand All @@ -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
Expand Down
105 changes: 105 additions & 0 deletions test/resolvers/__snapshots__/ant-design-vue.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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\\"]]);

",
}
`;