Skip to content

Commit

Permalink
feat: customElement option support for Vue 3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jul 23, 2021
1 parent 233af52 commit e19fcda
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 7 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
- [Documentation](https://vue-loader.vuejs.org)

## v16 Only Options

- `refSugar: boolean`: enable experimental ref sugar.
- `customElement: boolean | RegExp`: enable custom elements mode.
- Default is `/\.ce\.vue$/`
- Setting to `true` will load all `.vue` files as native Custom Elements.

## What is Vue Loader?

`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):
Expand Down
34 changes: 29 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
SFCBlock,
SFCTemplateCompileOptions,
SFCScriptCompileOptions,
SFCStyleBlock,
} from '@vue/compiler-sfc'
import { selectBlock } from './select'
import { genHotReloadCode } from './hotReload'
Expand All @@ -41,6 +40,8 @@ export interface VueLoaderOptions {
compiler?: TemplateCompiler | string
compilerOptions?: CompilerOptions
refSugar?: boolean
customElement?: boolean | RegExp

hotReload?: boolean
exposeFilename?: boolean
appendExtension?: boolean
Expand Down Expand Up @@ -98,6 +99,11 @@ export default function loader(
sourceMap,
})

const asCustomElement =
typeof options.customElement === 'boolean'
? options.customElement
: (options.customElement || /\.ce\.vue$/).test(filename)

// cache descriptor
setDescriptor(filename, descriptor)

Expand Down Expand Up @@ -184,15 +190,21 @@ export default function loader(
if (descriptor.styles.length) {
descriptor.styles
.filter((style) => style.src || nonWhitespaceRE.test(style.content))
.forEach((style: SFCStyleBlock, i: number) => {
.forEach((style, i) => {
const src = style.src || resourcePath
const attrsQuery = attrsToQuery(style.attrs, 'css')
// make sure to only pass id when necessary so that we don't inject
// duplicate tags when multiple components import the same css file
const idQuery = !style.src || style.scoped ? `&id=${id}` : ``
const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${resourceQuery}`
const inlineQuery = asCustomElement ? `&inline` : ``
const query = `?vue&type=style&index=${i}${idQuery}${inlineQuery}${attrsQuery}${resourceQuery}`
const styleRequest = stringifyRequest(src + query)
if (style.module) {
if (asCustomElement) {
loaderContext.emitError(
`<style module> is not supported in custom element mode.`
)
}
if (!hasCSSModules) {
stylesCode += `\nconst cssModules = script.__cssModules = {}`
hasCSSModules = true
Expand All @@ -205,10 +217,19 @@ export default function loader(
needsHotReload
)
} else {
stylesCode += `\nimport ${styleRequest}`
if (asCustomElement) {
stylesCode += `\nimport _style_${i} from ${styleRequest}`
} else {
stylesCode += `\nimport ${styleRequest}`
}
}
// TODO SSR critical CSS collection
})
if (asCustomElement) {
stylesCode += `\nscript.styles = [${descriptor.styles.map(
(_, i) => `_style_${i}`
)}]`
}
}

let code = [
Expand Down Expand Up @@ -264,7 +285,10 @@ export default function loader(
}

// finalize
code += `\n\nexport default script`
code += asCustomElement
? `\n\nimport { defineCustomElement as __ce } from 'vue';` +
`export default __ce(script)`
: `\n\nexport default script`
return code
}

Expand Down
10 changes: 8 additions & 2 deletions src/pitcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as loaderUtils from 'loader-utils'
const selfPath = require.resolve('./index')
// const templateLoaderPath = require.resolve('./templateLoader')
const stylePostLoaderPath = require.resolve('./stylePostLoader')
const styleInlineLoaderPath = require.resolve('./styleInlineLoader')

// @types/webpack doesn't provide the typing for loaderContext.loaders...
interface Loader {
Expand Down Expand Up @@ -64,12 +65,17 @@ export const pitch = function () {
if (query.type === `style`) {
const cssLoaderIndex = loaders.findIndex(isCSSLoader)
if (cssLoaderIndex > -1) {
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1)
// if inlined, ignore any loaders after css-loader and replace w/ inline
// loader
const afterLoaders =
query.inline != null
? [styleInlineLoaderPath]
: loaders.slice(0, cssLoaderIndex + 1)
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
return genProxyModule(
[...afterLoaders, stylePostLoaderPath, ...beforeLoaders],
context,
!!query.module
!!query.module || query.inline != null
)
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/styleInlineLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import webpack = require('webpack')

const StyleInineLoader: webpack.loader.Loader = function (source) {
// TODO minify this?
return `export default ${JSON.stringify(source)}`
}

export default StyleInineLoader

0 comments on commit e19fcda

Please sign in to comment.