Search code examples
webpackwebpack-2

Apply webpack loaders to a code fragment


Is there any way to make webpack to apply it's loaders to just a portion of a file? Here's a example of what I mean...

Let's say I have a bunch of loaders configured for .scss files. Would be awesome to be able write a component in any of the two following ways to produce the same output:


1) Standard loaders

component.js

import './component.scss';

export class Component {
...

component.scss

.component {
  background: #663399
}

...

2) Custom loaders

component.js

my-magic-import `
  .component {
    background: #663399
  }
`

export class Component {
...

How could I achieve something similar to the second case? Could I somehow modify the way webpack identify the requires, imports and file extensions so it could be able to understand my custom syntax? As last resort could one write a loader for js files that do this job for me?

Thanks for any hints about what should I read about to get this done.


Solution

  • You could try using these 2 libraries with webpack to convert to a cssobj first, then to plain CSS after that.

    https://github.com/cssobj/cssobj-converter (supports sass/less)

    var converter = require('cssobj-converter')
    var obj = converter('p { color: red; }') //obj is a css object
    

    https://github.com/cssobj/cssobj

    var cssobj = require('cssobj')
    var result = cssobj(obj) // the result is a CSS string
    

    You can write a globally unique function __insertCSS (in your app's index.js) which inserts plain CSS strings in the head element.

    Now these libraries only run on node, so you need one more step to make it work in the browser: a string replacement plugin

    https://www.npmjs.com/package/string-replace-webpack-plugin

    In webpack config you should configure the replace plugin to search for a pattern like /__insertCSS\(.*?\)/, get the string argument, process it like shown above and replace it with the result.

    In your webpack config:

    var StringReplacePlugin = require("string-replace-webpack-plugin");
    var converter = require('cssobj-converter')
    var cssobj = require('cssobj')
    
    function parseSass(str){
    var obj = converter(str) //obj is a css object
    return cssobj(obj) // the result is a CSS string
    }
    

    ...in module:

        loaders: [
                 // configure replacements for file patterns 
                 { 
                    test: /\.js(x?)$/,
                    loader: StringReplacePlugin.replace({
                        replacements: [
                            {
                                pattern: /__insertCSS\(`(.*?)`\)/ig,
                                replacement: function (match, p1, offset, string) {
                                   //p1 is the string enclosed by backticks
                                    var cssStr =  parseSass(p1); 
                                  //need to escape quotes with JSON in resulting string   
                                    return '__insertCSS("' + JSON.stringify(cssStr) + '")'
                                }
                            }
                        ]})
                    }
              ]
    

    Mind you, I have not tested that, but it seems feasible at a first look.

    This string replacement loader must come before babel, just put it first in the loaders array

    Doc here: https://webpack.github.io/docs/loaders.html#loader-order