Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack : How to convert variables on build

About using Vue (vue-loader) + Webpack and Chromatism

Example: (on dev / source)

let textColor = chromatism.contrastRatio('#ffea00').cssrgb // => rgb(0,0,0)

Does it possible to tell Webpack to convert to rgb(0,0,0) on build version?

So on build version should be converted something like: (for performance)

let textColor = 'rgb(0,0,0)'
like image 900
l2aelba Avatar asked Jun 26 '17 12:06

l2aelba


2 Answers

As previous answers and comments have already mentioned that there are no readily available AOT compilers to handle this kind of a situation (I mean this is a very specific case and no general purpose tool could be able to handle it), there is nothing stopping you from rolling out your own loader/plugin to handle this task!
You can use a custom Webpack Loader and Node's VM Module to execute the code at buildtime, get its output and replace it with the function call in your source file.
A sample implementation of this idea can look like the following snippet:

// file: chromatismOptimizer.js

// node's vm module https://nodejs.org/api/vm.html
const vm = require('vm')

const chromatism = require('chromatism')

// a basic and largley incomplete regex to extract lines where chromatism is called
let regex = /^(.*)(chromatism\S*)(.*)$/

// create a Sandbox 
//https://nodejs.org/api/vm.html#vm_what_does_it_mean_to_contextify_an_object
// this is roughly equivalent to the global the context the script will execute in
let sandbox = {
  chromatism: chromatism
}

// now create an execution context for the script
let context = new vm.createContext(sandbox)

// export a webpack sync loader function
module.exports = function chromatismOptimizer(source){
  let compiled = source.split('\n').reduce((agg, line) => {
    let parsed = line.replace(regex, (ig, x, source, z) => {
      // parse and execute the script inside the context
      // return value is the result of execution
      // https://nodejs.org/api/vm.html#vm_script_runincontext_contextifiedsandbox_options
      let res = (new (vm.Script)(source)).runInContext(context)
      return `${x}'${res}'${z? z : ''}`
    })
    agg.push(parsed)
    return agg;
  }, []).join('\n');

  return compiled;
}

Then in your production.webpack.js (or somefile like that) taken from this issue:

// Webpack config
resolveLoader: {
  alias: {
    'chromatism-optimizer': path.join(__dirname, './scripts/chromatism-optimizer'),
  },
}
// In module.rules
{
  test: /\.js$/,
  use: ['chromatism-optimizer'],
}

NOTE: This is just a reference implementation and is largely incomplete. The regex used here is quite basic and may not cover many other use cases, so make sure you update the regex. Also this whole stuff can be implemented using webpack plugins (it's just that I don't have sufficient knowledge of how to create one). For a quick starter refer to this wiki to learn how to create a custom plugin.

The basic idea is simple.

  1. First create a sandboxed environment,
    let sandbox = { chromatism:chromatism, ...}

  2. Then create an execution context,
    let context = new vm.createContext(sandbox)

  3. Then for each valid source, execute the source statement in the context and get the result.
    let result = (new (vm.Source)(source)).runInContext(context)

  4. Then maybe replace the original source statement with the result.

like image 169
riyaz-ali Avatar answered Oct 24 '22 08:10

riyaz-ali


Try using call-back loader to process all your JavaScript. Define a callback function for this specific case or even something more general like: evalDuringCompile: function(code) { return JSON.stringify(eval(code)); }

like image 44
Matjaž Drolc Avatar answered Oct 24 '22 08:10

Matjaž Drolc