diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 2e83d6d9..ff213fd7 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -13,7 +13,11 @@ import type * as _compiler from 'vue/compiler-sfc' import { version } from '../package.json' import { resolveCompiler } from './compiler' import { parseVueRequest } from './utils/query' -import { getDescriptor, getSrcDescriptor } from './utils/descriptorCache' +import { + getDescriptor, + getSrcDescriptor, + getTempSrcDescriptor, +} from './utils/descriptorCache' import { getResolvedScript, typeDepToSFCMap } from './script' import { transformMain } from './main' import { handleHotUpdate, handleTypeDepChange } from './handleHotUpdate' @@ -219,6 +223,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { } const { filename, query } = parseVueRequest(id) + // select corresponding block for sub-part virtual modules if (query.vue) { if (query.src) { @@ -248,9 +253,11 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { transform(code, id, opt) { const ssr = opt?.ssr === true const { filename, query } = parseVueRequest(id) + if (query.raw || query.url) { return } + if (!filter(filename) && !query.vue) { if ( !query.vue && @@ -278,7 +285,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { } else { // sub block request const descriptor = query.src - ? getSrcDescriptor(filename, query)! + ? getSrcDescriptor(filename, query) || + getTempSrcDescriptor(filename, query) : getDescriptor(filename, options)! if (query.type === 'template') { @@ -287,7 +295,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { return transformStyle( code, descriptor, - Number(query.index), + Number(query.index || 0), options, this, filename, diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index 08316454..297f9648 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -81,6 +81,25 @@ export function getSrcDescriptor( return cache.get(filename)! } +export function getTempSrcDescriptor( + filename: string, + query: VueQuery, +): SFCDescriptor { + // this is only used for pre-compiled <style src> with scoped flag + return { + filename, + id: query.id || '', + styles: [ + { + scoped: query.scoped, + loc: { + start: { line: 0, column: 0 }, + }, + }, + ], + } as SFCDescriptor +} + export function setSrcDescriptor( filename: string, entry: SFCDescriptor, diff --git a/packages/plugin-vue/src/utils/query.ts b/packages/plugin-vue/src/utils/query.ts index 66fe103c..29417a22 100644 --- a/packages/plugin-vue/src/utils/query.ts +++ b/packages/plugin-vue/src/utils/query.ts @@ -7,6 +7,7 @@ export interface VueQuery { raw?: boolean url?: boolean scoped?: boolean + id?: string } export function parseVueRequest(id: string): { diff --git a/playground/vue/Main.vue b/playground/vue/Main.vue index 9ab11aac..fec9c40e 100644 --- a/playground/vue/Main.vue +++ b/playground/vue/Main.vue @@ -30,6 +30,9 @@ <Url /> <TsGeneric msg="hello" /> <DefaultLangs /> + <PreCompiled /> + <PreCompiledExternalScoped /> + <PreCompiledExternalCssModules /> </template> <script setup lang="ts"> @@ -54,6 +57,9 @@ import { ref } from 'vue' import Url from './Url.vue' import TypeProps from './TypeProps.vue' import DefaultLangs from './DefaultLangs.vue' +import PreCompiled from './pre-compiled/foo.vue' +import PreCompiledExternalScoped from './pre-compiled/external-scoped.vue' +import PreCompiledExternalCssModules from './pre-compiled/external-cssmodules.vue' const TsGeneric = defineAsyncComponent(() => import('./TsGeneric.vue')) diff --git a/playground/vue/__tests__/vue.spec.ts b/playground/vue/__tests__/vue.spec.ts index 850e7cfe..f8e9a4ba 100644 --- a/playground/vue/__tests__/vue.spec.ts +++ b/playground/vue/__tests__/vue.spec.ts @@ -344,3 +344,15 @@ describe('default langs', () => { expect(await getColor('.default-langs')).toBe('blue') }) }) + +describe('pre-compiled components', () => { + test('should work', async () => { + expect(await getColor('.pre-compiled-title')).toBe('red') + }) + test('should work with external scoped style', async () => { + expect(await getColor('.pre-compiled-external-scoped-title')).toBe('red') + }) + test('should work with external css modules', async () => { + expect(await getColor('.pre-compiled-external-cssmodules')).toBe('red') + }) +}) diff --git a/playground/vue/pre-compiled/external-cssmodules.vue.js b/playground/vue/pre-compiled/external-cssmodules.vue.js new file mode 100644 index 00000000..3da12be5 --- /dev/null +++ b/playground/vue/pre-compiled/external-cssmodules.vue.js @@ -0,0 +1,59 @@ +import { + Fragment as _Fragment, + createElementBlock as _createElementBlock, + createElementVNode as _createElementVNode, + normalizeClass as _normalizeClass, + openBlock as _openBlock, +} from 'vue' + +import style0 from './external.module.css?module=true&lang.module.css' + +const __sfc__ = { + data() { + return { + isRed: true, + } + }, +} + +function render(_ctx, _cache, $props, $setup, $data, $options) { + return ( + _openBlock(), + _createElementBlock( + _Fragment, + null, + [ + _createElementVNode( + 'p', + { + class: _normalizeClass({ + [_ctx.$style.red]: $data.isRed, + 'pre-compiled-external-cssmodules': true, + }), + }, + ' Am I red? ', + 2 /* CLASS */, + ), + _createElementVNode( + 'p', + { + class: _normalizeClass([ + _ctx.$style.red, + _ctx.$style.bold, + 'pre-compiled-external-cssmodules', + ]), + }, + ' Red and bold ', + 2 /* CLASS */, + ), + ], + 64 /* STABLE_FRAGMENT */, + ) + ) +} +__sfc__.render = render +const cssModules = {} +cssModules['$style'] = style0 +__sfc__.__cssModules = cssModules +__sfc__.__file = 'external-cssmodules.vue' +export default __sfc__ diff --git a/playground/vue/pre-compiled/external-scoped.vue.js b/playground/vue/pre-compiled/external-scoped.vue.js new file mode 100644 index 00000000..5aa8254f --- /dev/null +++ b/playground/vue/pre-compiled/external-scoped.vue.js @@ -0,0 +1,61 @@ +import './external.css?vue&type=style&scoped=true&id=0d49ede6&src=0d49ede6&lang.css' + +import { + Fragment as _Fragment, + createElementBlock as _createElementBlock, + createElementVNode as _createElementVNode, + openBlock as _openBlock, + popScopeId as _popScopeId, + pushScopeId as _pushScopeId, + toDisplayString as _toDisplayString, + vModelText as _vModelText, + withDirectives as _withDirectives, + ref, +} from 'vue' +const __sfc__ = { + setup() { + const msg = ref('Hello World!') + return { msg } + }, +} + +const _withScopeId = (n) => ( + _pushScopeId('data-v-0d49ede6'), (n = n()), _popScopeId(), n +) +const _hoisted_1 = { class: 'pre-compiled-external-scoped-title' } + +function render(_ctx, _cache, $props, $setup, $data, $options) { + return ( + _openBlock(), + _createElementBlock( + _Fragment, + null, + [ + _createElementVNode( + 'h6', + _hoisted_1, + _toDisplayString($setup.msg), + 1 /* TEXT */, + ), + _withDirectives( + _createElementVNode( + 'input', + { + 'onUpdate:modelValue': + _cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)), + }, + null, + 512 /* NEED_PATCH */, + ), + [[_vModelText, $setup.msg]], + ), + ], + 64 /* STABLE_FRAGMENT */, + ) + ) +} +__sfc__.render = render + +__sfc__.__scopeId = 'data-v-0d49ede6' +__sfc__.__file = 'external-scoped.vue' +export default __sfc__ diff --git a/playground/vue/pre-compiled/external.css b/playground/vue/pre-compiled/external.css new file mode 100644 index 00000000..9e614629 --- /dev/null +++ b/playground/vue/pre-compiled/external.css @@ -0,0 +1,3 @@ +.pre-compiled-external-scoped-title { + color: red; +} diff --git a/playground/vue/pre-compiled/external.module.css b/playground/vue/pre-compiled/external.module.css new file mode 100644 index 00000000..03adb4b5 --- /dev/null +++ b/playground/vue/pre-compiled/external.module.css @@ -0,0 +1,6 @@ +.red { + color: red; +} +.bold { + font-weight: bold; +} diff --git a/playground/vue/pre-compiled/foo.vue.js b/playground/vue/pre-compiled/foo.vue.js new file mode 100644 index 00000000..8777b5e6 --- /dev/null +++ b/playground/vue/pre-compiled/foo.vue.js @@ -0,0 +1,55 @@ +import './foo.vue__0.css' + +import { + Fragment as _Fragment, + createElementBlock as _createElementBlock, + createElementVNode as _createElementVNode, + openBlock as _openBlock, + toDisplayString as _toDisplayString, + vModelText as _vModelText, + withDirectives as _withDirectives, + ref, +} from 'vue' +const __sfc__ = { + setup() { + const msg = ref('Hello World!') + return { msg } + }, +} + +const _hoisted_1 = { class: 'pre-compiled-title' } + +function render(_ctx, _cache, $props, $setup, $data, $options) { + return ( + _openBlock(), + _createElementBlock( + _Fragment, + null, + [ + _createElementVNode( + 'h6', + _hoisted_1, + _toDisplayString($setup.msg), + 1 /* TEXT */, + ), + _withDirectives( + _createElementVNode( + 'input', + { + 'onUpdate:modelValue': + _cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)), + }, + null, + 512 /* NEED_PATCH */, + ), + [[_vModelText, $setup.msg]], + ), + ], + 64 /* STABLE_FRAGMENT */, + ) + ) +} +__sfc__.render = render + +__sfc__.__file = 'foo.vue' +export default __sfc__ diff --git a/playground/vue/pre-compiled/foo.vue__0.css b/playground/vue/pre-compiled/foo.vue__0.css new file mode 100644 index 00000000..afa14cb5 --- /dev/null +++ b/playground/vue/pre-compiled/foo.vue__0.css @@ -0,0 +1,3 @@ +.pre-compiled-title { + color: red; +}