How can I provide parameters for webpack html-loader interpolation?

前端 未结 7 1005
暖寄归人
暖寄归人 2020-12-05 02:36

In the html-loader documentation there is this example

require(\"html?interpolate=require!./file.ftl\");

<#list list as list>
    

        
相关标签:
7条回答
  • 2020-12-05 03:01

    You might laugh, but using default loaders provided with HTMLWebpackPlugin you could do string replacement on the HTML-partial file.

    1. index.html is ejs template (ejs is the default loader in HTMLWebpackPlugin)
    2. file.html is just an html string (loaded via html-loader also available by default with HTMLWebpackPlugin or maybe it comes with webpack?)

    Setup

    Just use the default ejs templating provided in HTMLWebpackPlugin

    new HtmlWebpackPlugin({
        template: 'src/views/index.ejs',
        filename: 'index.html',
        title: 'Home',
        chunks: ['index'],
        templateParameters(compilation, assets, options) {
            return {
                foo: 'bar'
            }
        }
    })
    
    

    Here's my top level ejs file

    // index.html 
    
    <html lang="en" dir="ltr">
        <head>
            <title><%=foo%></title>
        </head>
        <body>
            <%
                var template = require("html-loader!./file.html");
            %>
            <%= template.replace('${foo}',foo) %>
        </body>
    </html>
    

    Here's file.html, which html-loader exports as a string.

    // file.html 
    
    <h1>${foo}</h1>
    
    0 讨论(0)
  • 2020-12-05 03:02

    You can make it on your own: In html-loader plugin folder (in index.js) replace code by this

    /*
    	MIT License http://www.opensource.org/licenses/mit-license.php
    	Author Tobias Koppers @sokra
    */
    var htmlMinifier = require("html-minifier");
    var attrParse = require("./lib/attributesParser");
    var loaderUtils = require("loader-utils");
    var url = require("url");
    var assign = require("object-assign");
    var compile = require("es6-templates").compile;
    
    function randomIdent() {
    	return "xxxHTMLLINKxxx" + Math.random() + Math.random() + "xxx";
    }
    
    function getLoaderConfig(context) {
    	var query = loaderUtils.getOptions(context) || {};
    	var configKey = query.config || 'htmlLoader';
    	var config = context.options && context.options.hasOwnProperty(configKey) ? context.options[configKey] : {};
    
    	delete query.config;
    
    	return assign(query, config);
    }
    
    module.exports = function(content) {
    	this.cacheable && this.cacheable();
    	var config = getLoaderConfig(this);
    	var attributes = ["img:src"];
    	if(config.attrs !== undefined) {
    		if(typeof config.attrs === "string")
    			attributes = config.attrs.split(" ");
    		else if(Array.isArray(config.attrs))
    			attributes = config.attrs;
    		else if(config.attrs === false)
    			attributes = [];
    		else
    			throw new Error("Invalid value to config parameter attrs");
    	}
    	var root = config.root;
    	var links = attrParse(content, function(tag, attr) {
    		var res = attributes.find(function(a) {
    			if (a.charAt(0) === ':') {
    				return attr === a.slice(1);
    			} else {
    				return (tag + ":" + attr) === a;
    			}
    		});
    		return !!res;
    	});
    	links.reverse();
    	var data = {};
    	content = [content];
    	links.forEach(function(link) {
    		if(!loaderUtils.isUrlRequest(link.value, root)) return;
    
    		if (link.value.indexOf('mailto:') > -1 ) return;
    
    		var uri = url.parse(link.value);
    		if (uri.hash !== null && uri.hash !== undefined) {
    			uri.hash = null;
    			link.value = uri.format();
    			link.length = link.value.length;
    		}
    
    		do {
    			var ident = randomIdent();
    		} while(data[ident]);
    		data[ident] = link.value;
    		var x = content.pop();
    		content.push(x.substr(link.start + link.length));
    		content.push(ident);
    		content.push(x.substr(0, link.start));
    	});
    	content.reverse();
    	content = content.join("");
    
    	if (config.interpolate === 'require'){
    
    		var reg = /\$\{require\([^)]*\)\}/g;
    		var result;
    		var reqList = [];
    		while(result = reg.exec(content)){
    			reqList.push({
    				length : result[0].length,
    				start : result.index,
    				value : result[0]
    			})
    		}
    		reqList.reverse();
    		content = [content];
    		reqList.forEach(function(link) {
    			var x = content.pop();
    			do {
    				var ident = randomIdent();
    			} while(data[ident]);
    			data[ident] = link.value.substring(11,link.length - 3)
    			content.push(x.substr(link.start + link.length));
    			content.push(ident);
    			content.push(x.substr(0, link.start));
    		});
    		content.reverse();
    		content = content.join("");
    	}
    
    	if(typeof config.minimize === "boolean" ? config.minimize : this.minimize) {
    		var minimizeOptions = assign({}, config);
    
    		[
    			"removeComments",
    			"removeCommentsFromCDATA",
    			"removeCDATASectionsFromCDATA",
    			"collapseWhitespace",
    			"conservativeCollapse",
    			"removeAttributeQuotes",
    			"useShortDoctype",
    			"keepClosingSlash",
    			"minifyJS",
    			"minifyCSS",
    			"removeScriptTypeAttributes",
    			"removeStyleTypeAttributes",
    		].forEach(function(name) {
    			if(typeof minimizeOptions[name] === "undefined") {
    				minimizeOptions[name] = true;
    			}
    		});
    
    		content = htmlMinifier.minify(content, minimizeOptions);
    	}
    	
    	
    
    	if(config.interpolate && config.interpolate !== 'require') {
    		// Double escape quotes so that they are not unescaped completely in the template string
    		content = content.replace(/\\"/g, "\\\\\"");
    		content = content.replace(/\\'/g, "\\\\\'");
    		
    		content = JSON.stringify(content);
    		content = '`' + content.substring(1, content.length - 1) + '`';
    		
    		//content = compile('`' + content + '`').code;
    	} else {
    		content = JSON.stringify(content);
    	}
    	
    
        var exportsString = "module.exports = function({...data}){return ";
    	if (config.exportAsDefault) {
            exportsString = "exports.default = function({...data}){return ";
    	} else if (config.exportAsEs6Default) {
            exportsString = "export default function({...data}){return ";
    	}
    
     	return exportsString + content.replace(/xxxHTMLLINKxxx[0-9\.]+xxx/g, function(match) {
    		if(!data[match]) return match;
    		
    		var urlToRequest;
    
    		if (config.interpolate === 'require') {
    			urlToRequest = data[match];
    		} else {
    			urlToRequest = loaderUtils.urlToRequest(data[match], root);
    		}
    		
    		return ' + require(' + JSON.stringify(urlToRequest) + ') + ';
    	}) + "};";
    
    }

    0 讨论(0)
  • 2020-12-05 03:12

    Solution 1

    I found another solution, using html-loader with interpolate option.

    https://github.com/webpack-contrib/html-loader#interpolation

    { test: /\.(html)$/,
      include: path.join(__dirname, 'src/views'),
      use: {
        loader: 'html-loader',
        options: {
          interpolate: true
        }
      }
    }
    

    And then in html page you can import partials html and javascript variables.

    <!-- Importing top <head> section -->
    ${require('./partials/top.html')}
    <title>Home</title>
    </head>
    <body>
      <!-- Importing navbar -->
      ${require('./partials/nav.html')}
      <!-- Importing variable from javascript file -->
      <h1>${require('../js/html-variables.js').hello}</h1>
      <!-- Importing footer -->
      ${require('./partials/footer.html')}
    </body>
    

    The only downside is that you can't import other variables from HtmlWebpackPlugin like this <%= htmlWebpackPlugin.options.title %> (at least I can't find a way to import them) but for me it's not an issue, just write the title in your html or use a separate javascript file for handle variables.

    Solution 2

    Old answer

    Not sure if this is the right solution for you but I'll share my workflow (tested in Webpack 3).

    Instead of html-loader you can use this plugin github.com/bazilio91/ejs-compiled-loader:

    { test: /\.ejs$/, use: 'ejs-compiled-loader' }
    

    Change your .html files in .ejs and your HtmlWebpackPlugin to point to the right .ejs template:

    new HtmlWebpackPlugin({
        template: 'src/views/index.ejs',
        filename: 'index.html',
        title: 'Home',
        chunks: ['index']
    })
    

    You can import partials, variables, and assets in .ejs files:

    src/views/partials/head.ejs:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8"/>
      <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
      <meta name="viewport" content="width=device-width, initial-scale=1"/>
      <title><%= htmlWebpackPlugin.options.title %></title>
    </head>
    

    src/js/ejs_variables.js:

    const hello = 'Hello!';
    const bye = 'Bye!';
    
    export {hello, bye}
    

    src/views/index.ejs:

    <% include src/views/partials/head.ejs %>
    <body>    
      <h2><%= require("../js/ejs_variables.js").hello %></h2>
    
      <img src=<%= require("../../assets/sample_image.jpg") %> />
    
      <h2><%= require("../js/ejs_variables.js").bye %></h2>
    </body>
    

    A note, when you include a partial the path must be relative to the root of your project.

    0 讨论(0)
  • 2020-12-05 03:12

    I feel that Potench's's answer above should be the accepted one, but it comes with a caveat:

    Warning: the answer replaces htmlWebpackPlugin.options default object. Suggest augmenting, not replacing

    function templateParametersGenerator (compilation, assets, options) {
      return {
        compilation: compilation,
        webpack: compilation.getStats().toJson(),
        webpackConfig: compilation.options,
        htmlWebpackPlugin: {
          files: assets,
          options: options,
          // your extra parameters here
        }
      };
    }
    
    

    Source(s): 1 - https://github.com/jantimon/html-webpack-plugin/blob/8440e4e3af94ae5dced4901a13001c0628b9af87/index.js#L719-L729 2 - https://github.com/jantimon/html-webpack-plugin/issues/1004#issuecomment-411311939

    0 讨论(0)
  • 2020-12-05 03:14

    mustache-loader did the work for me:

    var html = require('mustache-loader!html-loader?interpolate!./index.html')({foo:'bar'});
    

    Then in your template you can use {{foo}}, and even insert other templates

    <h1>{{foo}}</h1>
    ${require('mustache-loader!html-loader?interpolate!./partial.html')({foo2: 'bar2'})}
    
    0 讨论(0)
  • 2020-12-05 03:16

    if you use template engine from htmlWebpackPlugin in partial, you can use like this:

      <!-- index.html -->
      <body>
        <div id="app"></div>
        <%= require('ejs-loader!./partial.gtm.html')({ htmlWebpackPlugin }) %>
      </body>
    
      <!-- partial.gtm.html -->
      <% if (GTM_TOKEN) { %>
      <noscript>
        <iframe
          src="https://www.googletagmanager.com/ns.html?id=<%= GTM_TOKEN %>"
          height="0"
          width="0"
          style="display:none;visibility:hidden"
        ></iframe>
      </noscript>
      <% } %>
    
      // webpack.config.json
      {
        plugins: [
          new webpack.DefinePlugin({
            GTM_TOKEN: process.env.GTM_TOKEN,
          }),
        ],
      }
    

    need npm i ejs-loader

    0 讨论(0)
提交回复
热议问题