Skip to content

Commit e19fcda

Browse files
committed
feat: customElement option support for Vue 3.2
1 parent 233af52 commit e19fcda

File tree

4 files changed

+52
-7
lines changed

4 files changed

+52
-7
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
55
- [Documentation](https://vue-loader.vuejs.org)
66

7+
## v16 Only Options
8+
9+
- `refSugar: boolean`: enable experimental ref sugar.
10+
- `customElement: boolean | RegExp`: enable custom elements mode.
11+
- Default is `/\.ce\.vue$/`
12+
- Setting to `true` will load all `.vue` files as native Custom Elements.
13+
714
## What is Vue Loader?
815

916
`vue-loader` is a loader for [webpack](https://webpack.js.org/) that allows you to author Vue components in a format called [Single-File Components (SFCs)](./docs/spec.md):

src/index.ts

+29-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
SFCBlock,
2222
SFCTemplateCompileOptions,
2323
SFCScriptCompileOptions,
24-
SFCStyleBlock,
2524
} from '@vue/compiler-sfc'
2625
import { selectBlock } from './select'
2726
import { genHotReloadCode } from './hotReload'
@@ -41,6 +40,8 @@ export interface VueLoaderOptions {
4140
compiler?: TemplateCompiler | string
4241
compilerOptions?: CompilerOptions
4342
refSugar?: boolean
43+
customElement?: boolean | RegExp
44+
4445
hotReload?: boolean
4546
exposeFilename?: boolean
4647
appendExtension?: boolean
@@ -98,6 +99,11 @@ export default function loader(
9899
sourceMap,
99100
})
100101

102+
const asCustomElement =
103+
typeof options.customElement === 'boolean'
104+
? options.customElement
105+
: (options.customElement || /\.ce\.vue$/).test(filename)
106+
101107
// cache descriptor
102108
setDescriptor(filename, descriptor)
103109

@@ -184,15 +190,21 @@ export default function loader(
184190
if (descriptor.styles.length) {
185191
descriptor.styles
186192
.filter((style) => style.src || nonWhitespaceRE.test(style.content))
187-
.forEach((style: SFCStyleBlock, i: number) => {
193+
.forEach((style, i) => {
188194
const src = style.src || resourcePath
189195
const attrsQuery = attrsToQuery(style.attrs, 'css')
190196
// make sure to only pass id when necessary so that we don't inject
191197
// duplicate tags when multiple components import the same css file
192198
const idQuery = !style.src || style.scoped ? `&id=${id}` : ``
193-
const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${resourceQuery}`
199+
const inlineQuery = asCustomElement ? `&inline` : ``
200+
const query = `?vue&type=style&index=${i}${idQuery}${inlineQuery}${attrsQuery}${resourceQuery}`
194201
const styleRequest = stringifyRequest(src + query)
195202
if (style.module) {
203+
if (asCustomElement) {
204+
loaderContext.emitError(
205+
`<style module> is not supported in custom element mode.`
206+
)
207+
}
196208
if (!hasCSSModules) {
197209
stylesCode += `\nconst cssModules = script.__cssModules = {}`
198210
hasCSSModules = true
@@ -205,10 +217,19 @@ export default function loader(
205217
needsHotReload
206218
)
207219
} else {
208-
stylesCode += `\nimport ${styleRequest}`
220+
if (asCustomElement) {
221+
stylesCode += `\nimport _style_${i} from ${styleRequest}`
222+
} else {
223+
stylesCode += `\nimport ${styleRequest}`
224+
}
209225
}
210226
// TODO SSR critical CSS collection
211227
})
228+
if (asCustomElement) {
229+
stylesCode += `\nscript.styles = [${descriptor.styles.map(
230+
(_, i) => `_style_${i}`
231+
)}]`
232+
}
212233
}
213234

214235
let code = [
@@ -264,7 +285,10 @@ export default function loader(
264285
}
265286

266287
// finalize
267-
code += `\n\nexport default script`
288+
code += asCustomElement
289+
? `\n\nimport { defineCustomElement as __ce } from 'vue';` +
290+
`export default __ce(script)`
291+
: `\n\nexport default script`
268292
return code
269293
}
270294

src/pitcher.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as loaderUtils from 'loader-utils'
55
const selfPath = require.resolve('./index')
66
// const templateLoaderPath = require.resolve('./templateLoader')
77
const stylePostLoaderPath = require.resolve('./stylePostLoader')
8+
const styleInlineLoaderPath = require.resolve('./styleInlineLoader')
89

910
// @types/webpack doesn't provide the typing for loaderContext.loaders...
1011
interface Loader {
@@ -64,12 +65,17 @@ export const pitch = function () {
6465
if (query.type === `style`) {
6566
const cssLoaderIndex = loaders.findIndex(isCSSLoader)
6667
if (cssLoaderIndex > -1) {
67-
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1)
68+
// if inlined, ignore any loaders after css-loader and replace w/ inline
69+
// loader
70+
const afterLoaders =
71+
query.inline != null
72+
? [styleInlineLoaderPath]
73+
: loaders.slice(0, cssLoaderIndex + 1)
6874
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
6975
return genProxyModule(
7076
[...afterLoaders, stylePostLoaderPath, ...beforeLoaders],
7177
context,
72-
!!query.module
78+
!!query.module || query.inline != null
7379
)
7480
}
7581
}

src/styleInlineLoader.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import webpack = require('webpack')
2+
3+
const StyleInineLoader: webpack.loader.Loader = function (source) {
4+
// TODO minify this?
5+
return `export default ${JSON.stringify(source)}`
6+
}
7+
8+
export default StyleInineLoader

0 commit comments

Comments
 (0)