├── .npmignore ├── .gitignore ├── .babelrc ├── lib ├── listToStyles.js ├── addStylesShadow.js ├── addStylesServer.js └── addStylesClient.js ├── .circleci └── config.yml ├── package.json ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── LICENSE ├── CHANGELOG.md ├── README.md ├── test └── test.js ├── index.js └── yarn.lock /.npmignore: -------------------------------------------------------------------------------- 1 | fixtures/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-es2015-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /lib/listToStyles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Translates the list format produced by css-loader into something 3 | * easier to manipulate. 4 | */ 5 | export default function listToStyles (parentId, list) { 6 | var styles = [] 7 | var newStyles = {} 8 | for (var i = 0; i < list.length; i++) { 9 | var item = list[i] 10 | var id = item[0] 11 | var css = item[1] 12 | var media = item[2] 13 | var sourceMap = item[3] 14 | var part = { 15 | id: parentId + ':' + i, 16 | css: css, 17 | media: media, 18 | sourceMap: sourceMap 19 | } 20 | if (!newStyles[id]) { 21 | styles.push(newStyles[id] = { id: id, parts: [part] }) 22 | } else { 23 | newStyles[id].parts.push(part) 24 | } 25 | } 26 | return styles 27 | } 28 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | # https://circleci.com/docs/2.0/circleci-images/#nodejs 6 | - image: circleci/node:6 7 | 8 | working_directory: ~/vue-style-loader 9 | 10 | steps: 11 | - checkout 12 | 13 | # Download and cache dependencies 14 | - restore_cache: 15 | keys: 16 | - v1-dependencies-{{ checksum "package.json" }} 17 | # fallback to using the latest cache if no exact match is found 18 | - v1-dependencies- 19 | 20 | - run: yarn install 21 | 22 | - save_cache: 23 | paths: 24 | - node_modules 25 | key: v1-dependencies-{{ checksum "package.json" }} 26 | 27 | # run tests! 28 | - run: yarn test 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-style-loader", 3 | "version": "4.1.3", 4 | "author": "Evan You", 5 | "description": "Vue.js style loader module for webpack", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:vuejs/vue-style-loader.git" 9 | }, 10 | "scripts": { 11 | "test": "jest", 12 | "prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s" 13 | }, 14 | "license": "MIT", 15 | "dependencies": { 16 | "hash-sum": "^1.0.2", 17 | "loader-utils": "^1.0.2" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.26.0", 21 | "babel-jest": "^22.1.0", 22 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 23 | "conventional-changelog-cli": "^2.0.1", 24 | "jest": "^22.1.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Do you want to request a *feature* or report a *bug*?** 4 | 5 | 6 | **What is the current behavior?** 7 | 8 | **If the current behavior is a bug, please provide the steps to reproduce.** 9 | 10 | 11 | **What is the expected behavior?** 12 | 13 | **If this is a feature request, what is motivation or use case for changing the behavior?** 14 | 15 | **Please mention other relevant information such as your webpack version, Node.js version and Operating System.** 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **What kind of change does this PR introduce?** 4 | 5 | 6 | **Did you add tests for your changes?** 7 | 8 | **If relevant, did you update the README?** 9 | 10 | **Summary** 11 | 12 | 13 | 14 | 15 | **Does this PR introduce a breaking change?** 16 | 17 | 18 | **Other information** 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/addStylesShadow.js: -------------------------------------------------------------------------------- 1 | import listToStyles from './listToStyles' 2 | 3 | export default function addStylesToShadowDOM (parentId, list, shadowRoot) { 4 | var styles = listToStyles(parentId, list) 5 | addStyles(styles, shadowRoot) 6 | } 7 | 8 | /* 9 | type StyleObject = { 10 | id: number; 11 | parts: Array 12 | } 13 | 14 | type StyleObjectPart = { 15 | css: string; 16 | media: string; 17 | sourceMap: ?string 18 | } 19 | */ 20 | 21 | function addStyles (styles /* Array */, shadowRoot) { 22 | const injectedStyles = 23 | shadowRoot._injectedStyles || 24 | (shadowRoot._injectedStyles = {}) 25 | for (var i = 0; i < styles.length; i++) { 26 | var item = styles[i] 27 | var style = injectedStyles[item.id] 28 | if (!style) { 29 | for (var j = 0; j < item.parts.length; j++) { 30 | addStyle(item.parts[j], shadowRoot) 31 | } 32 | injectedStyles[item.id] = true 33 | } 34 | } 35 | } 36 | 37 | function createStyleElement (shadowRoot) { 38 | var styleElement = document.createElement('style') 39 | styleElement.type = 'text/css' 40 | shadowRoot.appendChild(styleElement) 41 | return styleElement 42 | } 43 | 44 | function addStyle (obj /* StyleObjectPart */, shadowRoot) { 45 | var styleElement = createStyleElement(shadowRoot) 46 | var css = obj.css 47 | var media = obj.media 48 | var sourceMap = obj.sourceMap 49 | 50 | if (media) { 51 | styleElement.setAttribute('media', media) 52 | } 53 | 54 | if (sourceMap) { 55 | // https://developer.chrome.com/devtools/docs/javascript-debugging 56 | // this makes source maps inside style tags work properly in Chrome 57 | css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */' 58 | // http://stackoverflow.com/a/26603875 59 | css += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */' 60 | } 61 | 62 | if (styleElement.styleSheet) { 63 | styleElement.styleSheet.cssText = css 64 | } else { 65 | while (styleElement.firstChild) { 66 | styleElement.removeChild(styleElement.firstChild) 67 | } 68 | styleElement.appendChild(document.createTextNode(css)) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/addStylesServer.js: -------------------------------------------------------------------------------- 1 | import listToStyles from './listToStyles' 2 | 3 | export default function addStylesServer (parentId, list, isProduction, context) { 4 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 5 | context = __VUE_SSR_CONTEXT__ 6 | } 7 | if (context) { 8 | if (!context.hasOwnProperty('styles')) { 9 | Object.defineProperty(context, 'styles', { 10 | enumerable: true, 11 | get: function() { 12 | return renderStyles(context._styles) 13 | } 14 | }) 15 | // expose renderStyles for vue-server-renderer (vuejs/#6353) 16 | context._renderStyles = renderStyles 17 | } 18 | 19 | var styles = context._styles || (context._styles = {}) 20 | list = listToStyles(parentId, list) 21 | if (isProduction) { 22 | addStyleProd(styles, list) 23 | } else { 24 | addStyleDev(styles, list) 25 | } 26 | } 27 | } 28 | 29 | // In production, render as few style tags as possible. 30 | // (mostly because IE9 has a limit on number of style tags) 31 | function addStyleProd (styles, list) { 32 | for (var i = 0; i < list.length; i++) { 33 | var parts = list[i].parts 34 | for (var j = 0; j < parts.length; j++) { 35 | var part = parts[j] 36 | // group style tags by media types. 37 | var id = part.media || 'default' 38 | var style = styles[id] 39 | if (style) { 40 | if (style.ids.indexOf(part.id) < 0) { 41 | style.ids.push(part.id) 42 | style.css += '\n' + part.css 43 | } 44 | } else { 45 | styles[id] = { 46 | ids: [part.id], 47 | css: part.css, 48 | media: part.media 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | // In dev we use individual style tag for each module for hot-reload 56 | // and source maps. 57 | function addStyleDev (styles, list) { 58 | for (var i = 0; i < list.length; i++) { 59 | var parts = list[i].parts 60 | for (var j = 0; j < parts.length; j++) { 61 | var part = parts[j] 62 | styles[part.id] = { 63 | ids: [part.id], 64 | css: part.css, 65 | media: part.media 66 | } 67 | } 68 | } 69 | } 70 | 71 | function renderStyles (styles) { 72 | var css = '' 73 | for (var key in styles) { 74 | var style = styles[key] 75 | css += '' 78 | } 79 | return css 80 | } 81 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [4.1.3](https://github.com/vuejs/vue-style-loader/compare/v4.0.1...v4.1.3) (2021-03-03) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * also support passed ES modules from `css-loader` in addition to CommonJS format ([#47](https://github.com/vuejs/vue-style-loader/issues/47)) ([8b584bd](https://github.com/vuejs/vue-style-loader/commit/8b584bd)) 8 | * es module interop in HMR code ([8bc2fe3](https://github.com/vuejs/vue-style-loader/commit/8bc2fe3)) 9 | 10 | 11 | 12 | 13 | ## [4.1.2](https://github.com/vuejs/vue-style-loader/compare/v4.1.1...v4.1.2) (2018-08-13) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * fix inconsistent hashes between Windows and POSIX systems ([#28](https://github.com/vuejs/vue-style-loader/issues/28)) ([cf8b6e8](https://github.com/vuejs/vue-style-loader/commit/cf8b6e8)) 19 | 20 | 21 | 22 | 23 | ## [4.1.1](https://github.com/vuejs/vue-style-loader/compare/v4.1.0...v4.1.1) (2018-07-17) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * fix addStyleShadow when same style object is inserted multiple times ([12846a6](https://github.com/vuejs/vue-style-loader/commit/12846a6)) 29 | 30 | 31 | 32 | 33 | # [4.1.0](https://github.com/vuejs/vue-style-loader/compare/v4.0.2...v4.1.0) (2018-03-20) 34 | 35 | 36 | ### Features 37 | 38 | * support vue-loader 15 ([0c7ee9d](https://github.com/vuejs/vue-style-loader/commit/0c7ee9d)) 39 | 40 | 41 | 42 | 43 | ## [4.0.2](https://github.com/vuejs/vue-style-loader/compare/v4.0.1...v4.0.2) (2018-02-13) 44 | 45 | 46 | 47 | 48 | ## [4.0.1](https://github.com/vuejs/vue-style-loader/compare/v4.0.0...v4.0.1) (2018-01-31) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * typo ([00087b7](https://github.com/vuejs/vue-style-loader/commit/00087b7)) 54 | 55 | 56 | 57 | 58 | # [4.0.0](https://github.com/vuejs/vue-style-loader/compare/v3.1.1...v4.0.0) (2018-01-31) 59 | 60 | 61 | ### Features 62 | 63 | * shadowMode ([94737e5](https://github.com/vuejs/vue-style-loader/commit/94737e5)) 64 | * use ESM for runtime files ([18d0ae4](https://github.com/vuejs/vue-style-loader/commit/18d0ae4)) 65 | 66 | 67 | 68 | 69 | ## [3.1.1](https://github.com/vuejs/vue-style-loader/compare/v3.1.0...v3.1.1) (2018-01-24) 70 | 71 | 72 | 73 | 74 | # [3.1.0](https://github.com/vuejs/vue-style-loader/compare/v3.0.3...v3.1.0) (2018-01-24) 75 | 76 | 77 | ### Features 78 | 79 | * add `ssrId` option for rendering ssr id on client ([5281305](https://github.com/vuejs/vue-style-loader/commit/5281305)) 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-style-loader [![Build Status](https://circleci.com/gh/vuejs/vue-style-loader/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/vue-loader/tree/master) [![npm package](https://img.shields.io/npm/v/vue-style-loader.svg)](https://www.npmjs.com/package/vue-style-loader) 2 | 3 | This is a fork based on [style-loader](https://github.com/webpack/style-loader). Similar to `style-loader`, you can chain it after `css-loader` to dynamically inject CSS into the document as style tags. However, since this is included as a dependency and used by default in `vue-loader`, in most cases you don't need to configure this loader yourself. 4 | 5 | ## Options 6 | 7 | - **manualInject** (3.1.0+): 8 | 9 | Type: `boolean`. When importing the style from a non-vue-file, by default the style is injected as a side effect of the import. When `manualInject` is true, the imported style object exposes a `__inject__` method, which can then be called manually at appropriate timing. If called on the server, the method expects one argument which is the `ssrContext` to attach styles to. 10 | 11 | ``` js 12 | import styles from 'styles.scss' 13 | 14 | export default { 15 | beforeCreate() { // or create a mixin for this purpose 16 | if(styles.__inject__) { 17 | styles.__inject__(this.$ssrContext) 18 | } 19 | } 20 | 21 | render() { 22 | return
Hello World
23 | } 24 | } 25 | ``` 26 | 27 | Note this behavior is enabled automatically when `vue-style-loader` is used on styles imported within a `*.vue` file. The option is only exposed for advanced usage. 28 | 29 | - **ssrId** (3.1.0+): 30 | 31 | Type: `boolean`. Add `data-vue-ssr-id` attribute to injected `` + 50 | `` + 51 | `` + 52 | `` 53 | ) 54 | }) 55 | 56 | test('addStylesServer (prod)', () => { 57 | const context = global.__VUE_SSR_CONTEXT__ = {} 58 | addStylesServer('foo', mockedList, true) 59 | expect(context.styles).toBe( 60 | `` + 63 | `` 64 | ) 65 | }) 66 | 67 | // --- helpers --- 68 | 69 | function assertStylesMatch (list) { 70 | const styles = document.querySelectorAll('style') 71 | expect(styles.length).toBe(list.length) 72 | ;[].forEach.call(styles, (style, i) => { 73 | expect(style.textContent.indexOf(list[i][1]) > -1).toBe(true) 74 | }) 75 | } 76 | 77 | function mockSSRTags (list, parentId) { 78 | list.forEach((item, i) => { 79 | const style = document.createElement('style') 80 | style.setAttribute('data-vue-ssr-id', `${parentId}:${i}`) 81 | style.textContent = item[1] 82 | if (item[2]) { 83 | style.setAttribute('media', item[2]) 84 | } 85 | document.head.appendChild(style) 86 | }) 87 | } 88 | 89 | function mockProdSSRTags (list, parentId) { 90 | const style = document.createElement('style') 91 | style.setAttribute('data-vue-ssr-id', list.map((item, i) => `${parentId}:${i}`).join(' ')) 92 | style.textContent = list.map(item => item[1]).join('\n') 93 | document.head.appendChild(style) 94 | } 95 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | Modified by Evan You @yyx990803 5 | */ 6 | var loaderUtils = require('loader-utils') 7 | var path = require('path') 8 | var hash = require('hash-sum') 9 | var qs = require('querystring') 10 | 11 | module.exports = function () {} 12 | 13 | module.exports.pitch = function (remainingRequest) { 14 | var isServer = this.target === 'node' 15 | var isProduction = this.minimize || process.env.NODE_ENV === 'production' 16 | var addStylesClientPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesClient.js')) 17 | var addStylesServerPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesServer.js')) 18 | var addStylesShadowPath = loaderUtils.stringifyRequest(this, '!' + path.join(__dirname, 'lib/addStylesShadow.js')) 19 | 20 | var request = loaderUtils.stringifyRequest(this, '!!' + remainingRequest) 21 | var relPath = path.relative(__dirname, this.resourcePath).replace(/\\/g, '/') 22 | var id = JSON.stringify(hash(request + relPath)) 23 | var options = loaderUtils.getOptions(this) || {} 24 | 25 | // direct css import from js --> direct, or manually call `styles.__inject__(ssrContext)` with `manualInject` option 26 | // css import from vue file --> component lifecycle linked 27 | // style embedded in vue file --> component lifecycle linked 28 | var isVue = ( 29 | /"vue":true/.test(remainingRequest) || 30 | options.manualInject || 31 | qs.parse(this.resourceQuery.slice(1)).vue != null 32 | ) 33 | 34 | var shared = [ 35 | '// style-loader: Adds some css to the DOM by adding a