Skip to content

Commit bd5055d

Browse files
feat: support generated JS imports for external scoped style (#196)
Co-authored-by: Haoqun Jiang <[email protected]>
1 parent 5b2f9c8 commit bd5055d

File tree

11 files changed

+236
-3
lines changed

11 files changed

+236
-3
lines changed

packages/plugin-vue/src/index.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import type * as _compiler from 'vue/compiler-sfc'
1313
import { version } from '../package.json'
1414
import { resolveCompiler } from './compiler'
1515
import { parseVueRequest } from './utils/query'
16-
import { getDescriptor, getSrcDescriptor } from './utils/descriptorCache'
16+
import {
17+
getDescriptor,
18+
getSrcDescriptor,
19+
getTempSrcDescriptor,
20+
} from './utils/descriptorCache'
1721
import { getResolvedScript, typeDepToSFCMap } from './script'
1822
import { transformMain } from './main'
1923
import { handleHotUpdate, handleTypeDepChange } from './handleHotUpdate'
@@ -219,6 +223,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
219223
}
220224

221225
const { filename, query } = parseVueRequest(id)
226+
222227
// select corresponding block for sub-part virtual modules
223228
if (query.vue) {
224229
if (query.src) {
@@ -248,9 +253,11 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
248253
transform(code, id, opt) {
249254
const ssr = opt?.ssr === true
250255
const { filename, query } = parseVueRequest(id)
256+
251257
if (query.raw || query.url) {
252258
return
253259
}
260+
254261
if (!filter(filename) && !query.vue) {
255262
if (
256263
!query.vue &&
@@ -278,7 +285,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
278285
} else {
279286
// sub block request
280287
const descriptor = query.src
281-
? getSrcDescriptor(filename, query)!
288+
? getSrcDescriptor(filename, query) ||
289+
getTempSrcDescriptor(filename, query)
282290
: getDescriptor(filename, options)!
283291

284292
if (query.type === 'template') {
@@ -287,7 +295,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
287295
return transformStyle(
288296
code,
289297
descriptor,
290-
Number(query.index),
298+
Number(query.index || 0),
291299
options,
292300
this,
293301
filename,

packages/plugin-vue/src/utils/descriptorCache.ts

+19
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,25 @@ export function getSrcDescriptor(
8181
return cache.get(filename)!
8282
}
8383

84+
export function getTempSrcDescriptor(
85+
filename: string,
86+
query: VueQuery,
87+
): SFCDescriptor {
88+
// this is only used for pre-compiled <style src> with scoped flag
89+
return {
90+
filename,
91+
id: query.id || '',
92+
styles: [
93+
{
94+
scoped: query.scoped,
95+
loc: {
96+
start: { line: 0, column: 0 },
97+
},
98+
},
99+
],
100+
} as SFCDescriptor
101+
}
102+
84103
export function setSrcDescriptor(
85104
filename: string,
86105
entry: SFCDescriptor,

packages/plugin-vue/src/utils/query.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface VueQuery {
77
raw?: boolean
88
url?: boolean
99
scoped?: boolean
10+
id?: string
1011
}
1112

1213
export function parseVueRequest(id: string): {

playground/vue/Main.vue

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
<Url />
3131
<TsGeneric msg="hello" />
3232
<DefaultLangs />
33+
<PreCompiled />
34+
<PreCompiledExternalScoped />
35+
<PreCompiledExternalCssModules />
3336
</template>
3437

3538
<script setup lang="ts">
@@ -54,6 +57,9 @@ import { ref } from 'vue'
5457
import Url from './Url.vue'
5558
import TypeProps from './TypeProps.vue'
5659
import DefaultLangs from './DefaultLangs.vue'
60+
import PreCompiled from './pre-compiled/foo.vue'
61+
import PreCompiledExternalScoped from './pre-compiled/external-scoped.vue'
62+
import PreCompiledExternalCssModules from './pre-compiled/external-cssmodules.vue'
5763
5864
const TsGeneric = defineAsyncComponent(() => import('./TsGeneric.vue'))
5965

playground/vue/__tests__/vue.spec.ts

+12
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,15 @@ describe('default langs', () => {
344344
expect(await getColor('.default-langs')).toBe('blue')
345345
})
346346
})
347+
348+
describe('pre-compiled components', () => {
349+
test('should work', async () => {
350+
expect(await getColor('.pre-compiled-title')).toBe('red')
351+
})
352+
test('should work with external scoped style', async () => {
353+
expect(await getColor('.pre-compiled-external-scoped-title')).toBe('red')
354+
})
355+
test('should work with external css modules', async () => {
356+
expect(await getColor('.pre-compiled-external-cssmodules')).toBe('red')
357+
})
358+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {
2+
Fragment as _Fragment,
3+
createElementBlock as _createElementBlock,
4+
createElementVNode as _createElementVNode,
5+
normalizeClass as _normalizeClass,
6+
openBlock as _openBlock,
7+
} from 'vue'
8+
9+
import style0 from './external.module.css?module=true&lang.module.css'
10+
11+
const __sfc__ = {
12+
data() {
13+
return {
14+
isRed: true,
15+
}
16+
},
17+
}
18+
19+
function render(_ctx, _cache, $props, $setup, $data, $options) {
20+
return (
21+
_openBlock(),
22+
_createElementBlock(
23+
_Fragment,
24+
null,
25+
[
26+
_createElementVNode(
27+
'p',
28+
{
29+
class: _normalizeClass({
30+
[_ctx.$style.red]: $data.isRed,
31+
'pre-compiled-external-cssmodules': true,
32+
}),
33+
},
34+
' Am I red? ',
35+
2 /* CLASS */,
36+
),
37+
_createElementVNode(
38+
'p',
39+
{
40+
class: _normalizeClass([
41+
_ctx.$style.red,
42+
_ctx.$style.bold,
43+
'pre-compiled-external-cssmodules',
44+
]),
45+
},
46+
' Red and bold ',
47+
2 /* CLASS */,
48+
),
49+
],
50+
64 /* STABLE_FRAGMENT */,
51+
)
52+
)
53+
}
54+
__sfc__.render = render
55+
const cssModules = {}
56+
cssModules['$style'] = style0
57+
__sfc__.__cssModules = cssModules
58+
__sfc__.__file = 'external-cssmodules.vue'
59+
export default __sfc__
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import './external.css?vue&type=style&scoped=true&id=0d49ede6&src=0d49ede6&lang.css'
2+
3+
import {
4+
Fragment as _Fragment,
5+
createElementBlock as _createElementBlock,
6+
createElementVNode as _createElementVNode,
7+
openBlock as _openBlock,
8+
popScopeId as _popScopeId,
9+
pushScopeId as _pushScopeId,
10+
toDisplayString as _toDisplayString,
11+
vModelText as _vModelText,
12+
withDirectives as _withDirectives,
13+
ref,
14+
} from 'vue'
15+
const __sfc__ = {
16+
setup() {
17+
const msg = ref('Hello World!')
18+
return { msg }
19+
},
20+
}
21+
22+
const _withScopeId = (n) => (
23+
_pushScopeId('data-v-0d49ede6'), (n = n()), _popScopeId(), n
24+
)
25+
const _hoisted_1 = { class: 'pre-compiled-external-scoped-title' }
26+
27+
function render(_ctx, _cache, $props, $setup, $data, $options) {
28+
return (
29+
_openBlock(),
30+
_createElementBlock(
31+
_Fragment,
32+
null,
33+
[
34+
_createElementVNode(
35+
'h6',
36+
_hoisted_1,
37+
_toDisplayString($setup.msg),
38+
1 /* TEXT */,
39+
),
40+
_withDirectives(
41+
_createElementVNode(
42+
'input',
43+
{
44+
'onUpdate:modelValue':
45+
_cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)),
46+
},
47+
null,
48+
512 /* NEED_PATCH */,
49+
),
50+
[[_vModelText, $setup.msg]],
51+
),
52+
],
53+
64 /* STABLE_FRAGMENT */,
54+
)
55+
)
56+
}
57+
__sfc__.render = render
58+
59+
__sfc__.__scopeId = 'data-v-0d49ede6'
60+
__sfc__.__file = 'external-scoped.vue'
61+
export default __sfc__
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.pre-compiled-external-scoped-title {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.red {
2+
color: red;
3+
}
4+
.bold {
5+
font-weight: bold;
6+
}
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import './foo.vue__0.css'
2+
3+
import {
4+
Fragment as _Fragment,
5+
createElementBlock as _createElementBlock,
6+
createElementVNode as _createElementVNode,
7+
openBlock as _openBlock,
8+
toDisplayString as _toDisplayString,
9+
vModelText as _vModelText,
10+
withDirectives as _withDirectives,
11+
ref,
12+
} from 'vue'
13+
const __sfc__ = {
14+
setup() {
15+
const msg = ref('Hello World!')
16+
return { msg }
17+
},
18+
}
19+
20+
const _hoisted_1 = { class: 'pre-compiled-title' }
21+
22+
function render(_ctx, _cache, $props, $setup, $data, $options) {
23+
return (
24+
_openBlock(),
25+
_createElementBlock(
26+
_Fragment,
27+
null,
28+
[
29+
_createElementVNode(
30+
'h6',
31+
_hoisted_1,
32+
_toDisplayString($setup.msg),
33+
1 /* TEXT */,
34+
),
35+
_withDirectives(
36+
_createElementVNode(
37+
'input',
38+
{
39+
'onUpdate:modelValue':
40+
_cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)),
41+
},
42+
null,
43+
512 /* NEED_PATCH */,
44+
),
45+
[[_vModelText, $setup.msg]],
46+
),
47+
],
48+
64 /* STABLE_FRAGMENT */,
49+
)
50+
)
51+
}
52+
__sfc__.render = render
53+
54+
__sfc__.__file = 'foo.vue'
55+
export default __sfc__
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.pre-compiled-title {
2+
color: red;
3+
}

0 commit comments

Comments
 (0)