diff --git a/index.js b/index.js index bedb9c32..0bcb0f55 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,7 @@ module.exports.pitch = function (remainingRequest) { var isProduction = this.minimize || process.env.NODE_ENV === 'production' var addStylesClientPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesClient.js')) var addStylesServerPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesServer.js')) + var addStylesShadowPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesShadow.js')) var request = loaderUtils.stringifyRequest(this, '!!' + remainingRequest) var id = JSON.stringify(hash(request + path.relative(__dirname, this.resourcePath))) @@ -36,7 +37,17 @@ module.exports.pitch = function (remainingRequest) { 'if(content.locals) module.exports = content.locals;' ] - if (!isServer) { + // shadowMode is enabled in vue-cli with vue build --target web-component. + // exposes the same __inject__ method like SSR + if (options.shadowMode) { + return shared.concat([ + '// add CSS to Shadow Root', + 'var add = require(' + addStylesShadowPath + ').default', + 'module.exports.__inject__ = function (shadowRoot) {', + ' add(' + id + ', content, shadowRoot)', + '};' + ]).join('\n') + } else if (!isServer) { // on the client: dynamic inject + hot-reload var code = [ '// add the styles to the DOM', diff --git a/lib/addStylesShadow.js b/lib/addStylesShadow.js new file mode 100644 index 00000000..d7ae56ba --- /dev/null +++ b/lib/addStylesShadow.js @@ -0,0 +1,82 @@ +import listToStyles from './listToStyles' + +export default function addStylesToShadowDOM (parentId, list, shadowRoot) { + var styles = listToStyles(parentId, list) + addStyles(styles, shadowRoot) +} + +/* +type StyleObject = { + id: number; + parts: Array +} + +type StyleObjectPart = { + css: string; + media: string; + sourceMap: ?string +} +*/ + +function addStyles (styles /* Array */, shadowRoot) { + const injectedStyles = + shadowRoot._injectedStyles || + (shadowRoot._injectedStyles = {}) + for (var i = 0; i < styles.length; i++) { + var item = styles[i] + var style = injectedStyles[item.id] + if (style) { + style.refs++ + for (var j = 0; j < style.parts.length; j++) { + style.parts[j](item.parts[j]) + } + for (; j < item.parts.length; j++) { + style.parts.push(addStyle(item.parts[j], shadowRoot)) + } + if (style.parts.length > item.parts.length) { + style.parts.length = item.parts.length + } + } else { + var parts = [] + for (var j = 0; j < item.parts.length; j++) { + parts.push(addStyle(item.parts[j], shadowRoot)) + } + injectedStyles[item.id] = { id: item.id, refs: 1, parts: parts } + } + } +} + +function createStyleElement (shadowRoot) { + var styleElement = document.createElement('style') + styleElement.type = 'text/css' + shadowRoot.appendChild(styleElement) + return styleElement +} + +function addStyle (obj /* StyleObjectPart */, shadowRoot) { + var styleElement = createStyleElement(shadowRoot) + var css = obj.css + var media = obj.media + var sourceMap = obj.sourceMap + + if (media) { + styleElement.setAttribute('media', media) + } + + if (sourceMap) { + // https://developer.chrome.com/devtools/docs/javascript-debugging + // this makes source maps inside style tags work properly in Chrome + css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */' + // http://stackoverflow.com/a/26603875 + css += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */' + } + + if (styleElement.styleSheet) { + styleElement.styleSheet.cssText = css + } else { + while (styleElement.firstChild) { + styleElement.removeChild(styleElement.firstChild) + } + styleElement.appendChild(document.createTextNode(css)) + } +}