Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Including html partials within html partials without the interpolate option #291

Open
2 of 4 tasks
leo-petrucci opened this issue Jun 1, 2020 · 83 comments
Open
2 of 4 tasks

Comments

@leo-petrucci
Copy link

leo-petrucci commented Jun 1, 2020

Documentation Is:

  • Missing
  • Needed
  • Confusing
  • Not Sure?

Please Explain in Detail...

At some point in the past html-loader had an interpolate option. This allowed to load partials within partials in templates. As far as understand it this recently got deprecated and replaced with preprocessor.

So, answers like this one aren't working anymore.

A couple of questions like this one asking how to achieve it popped up, but they've gone without answers.

The documentation seems unclear as if this is still possible as the only example mentions handlebars and only shows this implementation within the webpack config.

Your Proposal for Changes

Is there any chance an official explanation of how to achieve this could be provided in the docs or even in this thread? I've tried everything possible and I'm absolutely lost.

@leo-petrucci
Copy link
Author

I ended up reverting to 0.5.5, which works without any problems. I'm unsure why this breaking change was included without a way to replace it?

@alexander-akait
Copy link
Member

alexander-akait commented Jun 2, 2020

Can you provide example how you want to include? You have JS, so you can include anything

@leo-petrucci
Copy link
Author

leo-petrucci commented Jun 2, 2020

Can you provide example how you want to include?

Including already works:

<%= require('html-loader!../components/navbar/index.html') %>

The problem is that without the interpolate option (as well as the root option), if one of the files I include has another inclusion within it, then it won't actually be included. This means I can't have a component within a component which severely limits what's possible.

Why not bring back how it used to work?:

<%= require('html-loader?root=.&interpolate!../components/sidebar/index.html') %>

You have JS, so you can include anything

If I include from JS it will be added client side, which is of course not great for SEO. I need Webpack to build all my files into a single component.

@alexander-akait
Copy link
Member

alexander-akait commented Jun 2, 2020

It is out of scope HTML spec, HTML doesn't support <=% require() %=>, so we do not support it, you need loader for this loader, also JS has import and it can be used with it

@alexander-akait
Copy link
Member

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

You should always return valid HTML

@leo-petrucci
Copy link
Author

It is out of scope HTML spec, HTML doesn't support <=% require() %=>, we doesn't support it, you need loader for this loader, also JS has import and it can be used with it

The html-loader already supports <=% require() %=>. Either way that's not my point.
I'm not asking to support <=% require() %=>, I'm asking to either:

  • Get an entry in the docs to explain how to properly replace interpolate with preprocessor to allow html components
  • Bring back interpolate so that interpolating html is possible

Which are not out of scope

@alexander-akait
Copy link
Member

alexander-akait commented Jun 2, 2020

Get an entry in the docs to explain how to properly replace interpolate with preprocessor to allow html components

We have examples for handlebars/PostHTML, it is a most popular solutions, we can't cover all edge cases

Bring back interpolate so that interpolating html is possible

No interpolating more, if you need it you can create own loader, we support only pure HTML, because it will be part of core webpack, webpack supports non standard stuff only through loaders/plugins

@alexander-akait
Copy link
Member

The html-loader already supports <=% require() %=>.

It is just luck that you did not receive an error while parsing.

@alexander-akait
Copy link
Member

alexander-akait commented Jun 2, 2020

I strongly recommend migrate on a template system, using require is the bad solution and have many bugs and limitations

@leo-petrucci
Copy link
Author

Would it be a problem if I spun html-loader 0.5.5 (the last version that supported this) into its own loader?

The demand is there and people clearly prefer using this rather than learning some other templating system. It's also the suggested way by webpack-html-plugin to load partial HTML files, so it seems a bit closeminded to say

It is just luck that you did not receive an error while parsing.

As far as I see it there's not really enough information on how to use preprocessor. It would probably serve the function I'm asking fairly well, it's just unclear how I could set preprocessor to use a normal loader rather than another library to process my files.

On top of that the changelog states:

the interpolate option was removed, please consider migration on the preprocessor

But doesn't offer much explanation on how to do so.

@alexander-akait
Copy link
Member

Would it be a problem if I spun html-loader 0.5.5 (the last version that supported this) into its own loader?

Yep, you can do it temporary, but I would advise speed up migration on a template system

The demand is there and people clearly prefer using this rather than learning some other templating system. It's also the suggested way by webpack-html-plugin to load partial HTML files, so it seems a bit closeminded to say

We should remove it from examples

As far as I see it there's not really enough information on how to use preprocessor. It would probably serve the function I'm asking fairly well, it's just unclear how I could set preprocessor to use a normal loader rather than another library to process my files.

Yep, we can improve docs about it.

On top of that the changelog states:

the interpolate option was removed, please consider migration on the preprocessor
But doesn't offer much explanation on how to do so.

I provide examples for handlebars/PostHTML, I thought that was enough, maybe improving it for compatibility with other loaders will be great

@philippdieter
Copy link

The crucial part for me to have the included file processed by webpack again before being included. I can include the file with PostHTML, but it only loads the raw file. If there is a way to achieve this could you please explain how?

@alexander-akait
Copy link
Member

I can include the file with PostHTML, but it only loads the raw file.

What do you mean?

@philippdieter
Copy link

philippdieter commented Jun 3, 2020

What do you mean?

I set up posthtml and posthtml-include created a file file1.html with includes <include src="file2.vue"></include><include src="file2.pug"></include>. As output I got the content of file2.vuefile2.pug insertet into the html of file1.html.

Including the old way processed the content of file2.vuepug via webpack and the configured loaders.

@alexander-akait
Copy link
Member

@philippdieter Maybe you can provide small example of configuration? I want to make sure that I understand you correctly

@philippdieter
Copy link

I made the mistake and mixed up the endings of my files in the post above, I have updated them there, I am sorry.

I build an example located at
https://github.com/philippdieter/html-loader-include-sample

The resulting output file is
https://github.com/philippdieter/html-loader-include-sample/blob/master/webroot/file1.html

The output possible with include was <h1>Content file1</h1><h1>Content file2</h1> as the included file was processed by webpack, now its <h1>Content file1</h1>h1 Content file2

@alexander-akait
Copy link
Member

@philippdieter Thanks, I will look at this tomorrow

@alexander-akait
Copy link
Member

alexander-akait commented Jun 4, 2020

Yes, the same problem, here other problems most if loaders for HTML preprocessing returns JS content instead HTML, it's a little wrong, for example postcss-loader return CSS content, not JS, so css-loader + postcss-loader work fine

@alexander-akait
Copy link
Member

alexander-akait commented Jun 4, 2020

Ideally pug-loader should return pure HTML for html-loader, here no solutions, only open an issue in pug-loader, even include not help here, it is a fundamental problem.

In theory we can add some magic to html-loader, like:

<div>My Custom Text</div>
<link rel="webpack-import" href="./index.html" />
<div>My Custom Text</div>

Will be compiled to:

import __HTML_LOADER_LINK_IMPORT_1__ from './index.html';

export `<div>My Custom Text</div>` +__HTML_LOADER_LINK_IMPORT_1__ + '<div>My Custom Text</div>';

And extracted to

<div>My Custom Text</div>
<div><img alt="Image" src="./path/to/img.png"><span>Foo</span></div>
<div>My Custom Text</div>

It is HTML compatibility, no problems with require/import, maybe even no lint problems

@alexander-akait
Copy link
Member

I would like to hear your opinion.

@alexander-akait
Copy link
Member

Other good idea WICG/webcomponents#645

@alexander-akait
Copy link
Member

alexander-akait commented Jun 4, 2020

JS has imports/exports, CSS only imports, HTML has not imports/exports 😄

@philippdieter
Copy link

Just to be sure you looked at the right module: In my example it's pug-plain-loder, not pug-loader. I remember vaguely about having used this loader because it returns html(?).
I don't know enough about webpack internals to tell if there is a problem, all I know is that my setup worked with html-loader 0.5.5 and include ':D

I like the idea of the link tag very much.

As far as I understand WICG/webcomponents#645 it's about using includes on client side, right?
As my current workflow relies on having the result of the including written into an html file to disk I would much appreciate the solution with the link tag.

@alexander-akait
Copy link
Member

alexander-akait commented Jun 5, 2020

Just to be sure you looked at the right module: In my example it's pug-plain-loder, not pug-loader. I remember vaguely about having used this loader because it returns html(?).

Yes, I mean pug-plain-loader.

As far as I understand WICG/webcomponents#645 it's about using includes on client side, right?

Yes 👍

I would not want to make this part as a separate option or an official solution, as this is still not standardized.

But we can do something small and simple.

We just need improve the attributes option, like:

{
  tag: 'include',
  attribute: 'src',
  type: 'template',
},

You can:

  • Using any tags/attributes
  • No new options, it is just advanced abilities
  • We do not ship new non standard features for HTML

@philippdieter
Copy link

We just need improve the attributes option, like:

Having the option to specify a tag even sounds better than having a fixed one. I like this idea very much!

@alexander-akait
Copy link
Member

@ItalyPaleAle Good idea, but my recommendation is avoid interpolate 😄

@azamanaza
Copy link

azamanaza commented Nov 4, 2020

@lucianogreiner

For those who want a simple working partial support like this:

<include src="../file/partial1.html"/>
<include src="../file/partial2.html"></include>
{
  test: /\.html$/i,
  use: {
    loader: 'html-loader',
    options: {
      preprocessor: (content, loaderContext) => 
        content.replace(/\<include src=\"(.+)\"\/?\>(?:\<\/include\>)?/gi, 
          (m, src) => fs.readFileSync(path.resolve(loaderContext.context, src), 'utf8'))
    }
  } 
}

I made it recursive incase your partials have includes.

const INCLUDE_PATTERN = /\<include src=\"(.+)\"\/?\>(?:\<\/include\>)?/gi;
const processNestedHtml = (content, loaderContext) => !INCLUDE_PATTERN.test(content) ?
  content : content.replace(INCLUDE_PATTERN, (m, src) => processNestedHtml(fs.readFileSync(path.resolve(loaderContext.context, src), 'utf8'), loaderContext));
module: {
    rules: [
      {
        test: /\.html$/,
        use: {
          loader: 'html-loader',
          options: {
            preprocessor: processNestedHtml
          }
        }
      }
    ],
  },

@Poofikate
Copy link

@laozei6401 this will only work with one include per page.
if (!fs.existsSync(context)) { // return _; context = path.resolve(loaderContext.context, src); }
this will solve the problem

@laozei6401
Copy link

I found a problem, if you use replace, it will lead to the wrong path.

@panchoggit
Copy link

panchoggit commented Feb 1, 2021

@laozei6401 this will only work with one include per page.
if (!fs.existsSync(context)) { // return _; context = path.resolve(loaderContext.context, src); }
this will solve the problem

this solutions not working webpack 5
where????
any real solution???

@alexander-akait
Copy link
Member

alexander-akait commented Feb 15, 2021

With v2 we can implement using type as Function (possible to implement), but here one edge case - <img src="./assets/images.ext" alt="">, you need rewrite src, but it is not easy, but we have other solutions supported out of box for webpack v5, you can use <img src="/assets/images.ext" alt="">, webpack tries to resolve /assets/images.ext relative to context (i.e. context + '/assets/images.ext', default value of the context options is process.cwd()), so you can don't worry about URLs in src/srcset/etc.

@alexander-akait
Copy link
Member

#364

@paulocoutinhox
Copy link

paulocoutinhox commented Mar 28, 2021

Hi,

i tried all solutions here and nothing:

One:

<webpack-import src="./partials/header.html" />

Two:

${require("!!html-loader!./partials/header.html")}

Three:

${require("html-loader!./partials/header.html")}

Four:

<%= ${require("html-loader!./partials/header.html")} %>

Five (it include the file but images inside header.html not show):

<%= require("./partials/header.html").default %>

image

Package.json:

"html-loader": "^2.1.2",

Webpack (i tried all options on thread):

{
    test: /\.(html)$/,
    use: [{
        loader: 'html-loader',
    }],
},

How can i make it work?

@SamiDghim
Copy link

i downgrade to "html-loader": "^0.5.5" and it works fine for me

@laozei6401
Copy link

Hi,

i tried all solutions here and nothing:

One:

<webpack-import src="./partials/header.html" />

Two:

${require("!!html-loader!./partials/header.html")}

Three:

${require("html-loader!./partials/header.html")}

Four:

<%= ${require("html-loader!./partials/header.html")} %>

Five (it include the file but images inside header.html not show):

<%= require("./partials/header.html").default %>

image

Package.json:

"html-loader": "^2.1.2",

Webpack (i tried all options on thread):

{
    test: /\.(html)$/,
    use: [{
        loader: 'html-loader',
    }],
},

How can i make it work?

webpack-import pull了暂时没有发布
如果你安装了html-webpack-plugin,你可以这样写<%= require(路径)() %>

@paulocoutinhox
Copy link

paulocoutinhox commented Mar 29, 2021

Hi @laozei6401,

I tried:

"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.1",
<%= require("./partials/header.html")() %>

But it not render:
image

And my webpack .js

/**
 * Webpack main configuration file
 */

const path = require('path');
const fs = require('fs');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { extendDefaultPlugins } = require('svgo');

const environment = require('./configuration/environment');

const templateFiles = fs.readdirSync(environment.paths.source)
    .filter((file) => path.extname(file).toLowerCase() === '.html');

const htmlPluginEntries = templateFiles.map((template) => new HTMLWebpackPlugin({
    inject: 'body',
    hash: false,
    filename: template,
    template: path.resolve(environment.paths.source, template),
    favicon: path.resolve(environment.paths.source, 'images', 'favicon.ico'),
}));

module.exports = {
    entry: {
        app: path.resolve(environment.paths.source, 'js', 'app.js'),
    },
    output: {
        filename: 'js/[name].js',
        path: environment.paths.output,
    },
    module: {
        rules: [{
                test: /\.((c|sa|sc)ss)$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: ['babel-loader'],
            },
            {
                test: /\.(png|gif|jpe?g|svg)$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        name: 'images/[name].[hash:6].[ext]',
                        publicPath: '../',
                        limit: environment.limits.images,
                    },
                }, ],
            },
            {
                test: /\.(eot|ttf|woff|woff2)$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        name: 'fonts/[name].[hash:6].[ext]',
                        publicPath: '../',
                        limit: environment.limits.fonts,
                    },
                }, ],
            },
            {
                test: /\.(html)$/,
                use: "html-loader",
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/[name].css',
        }),
        new ImageMinimizerPlugin({
            test: /\.(jpe?g|png|gif|svg)$/i,
            minimizerOptions: {
                // Lossless optimization with custom option
                // Feel free to experiment with options for better result for you
                plugins: [
                    ['gifsicle', { interlaced: true }],
                    ['jpegtran', { progressive: true }],
                    ['optipng', { optimizationLevel: 5 }],
                    [
                        'svgo',
                        {
                            plugins: extendDefaultPlugins([{
                                name: 'removeViewBox',
                                active: false,
                            }, ]),
                        },
                    ],
                ],
            },
        }),
        new CleanWebpackPlugin({
            verbose: true,
            cleanOnceBeforeBuildPatterns: ['**/*', '!stats.json'],
        }),
        new CopyWebpackPlugin({
            patterns: [{
                from: path.resolve(environment.paths.source, 'site.webmanifest'),
                to: path.resolve(environment.paths.output, 'site.webmanifest'),
                toType: 'file',
                globOptions: {
                    ignore: ['*.DS_Store', 'Thumbs.db'],
                },
            }, {
                from: path.resolve(environment.paths.source, 'CNAME'),
                to: path.resolve(environment.paths.output, 'CNAME'),
                toType: 'file',
                globOptions: {
                    ignore: ['*.DS_Store', 'Thumbs.db'],
                },
            }, ],
        }),
    ].concat(htmlPluginEntries),
    target: 'web',
};

And my directory is:

index.html
partials/
-- header.html

image

@laozei6401
Copy link

Hi @laozei6401,

I tried:

"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.1",
<%= require("./partials/header.html")() %>

But it not render:
image

And my webpack .js

/**
 * Webpack main configuration file
 */

const path = require('path');
const fs = require('fs');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { extendDefaultPlugins } = require('svgo');

const environment = require('./configuration/environment');

const templateFiles = fs.readdirSync(environment.paths.source)
    .filter((file) => path.extname(file).toLowerCase() === '.html');

const htmlPluginEntries = templateFiles.map((template) => new HTMLWebpackPlugin({
    inject: 'body',
    hash: false,
    filename: template,
    template: path.resolve(environment.paths.source, template),
    favicon: path.resolve(environment.paths.source, 'images', 'favicon.ico'),
}));

module.exports = {
    entry: {
        app: path.resolve(environment.paths.source, 'js', 'app.js'),
    },
    output: {
        filename: 'js/[name].js',
        path: environment.paths.output,
    },
    module: {
        rules: [{
                test: /\.((c|sa|sc)ss)$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: ['babel-loader'],
            },
            {
                test: /\.(png|gif|jpe?g|svg)$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        name: 'images/[name].[hash:6].[ext]',
                        publicPath: '../',
                        limit: environment.limits.images,
                    },
                }, ],
            },
            {
                test: /\.(eot|ttf|woff|woff2)$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        name: 'fonts/[name].[hash:6].[ext]',
                        publicPath: '../',
                        limit: environment.limits.fonts,
                    },
                }, ],
            },
            {
                test: /\.(html)$/,
                use: "html-loader",
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/[name].css',
        }),
        new ImageMinimizerPlugin({
            test: /\.(jpe?g|png|gif|svg)$/i,
            minimizerOptions: {
                // Lossless optimization with custom option
                // Feel free to experiment with options for better result for you
                plugins: [
                    ['gifsicle', { interlaced: true }],
                    ['jpegtran', { progressive: true }],
                    ['optipng', { optimizationLevel: 5 }],
                    [
                        'svgo',
                        {
                            plugins: extendDefaultPlugins([{
                                name: 'removeViewBox',
                                active: false,
                            }, ]),
                        },
                    ],
                ],
            },
        }),
        new CleanWebpackPlugin({
            verbose: true,
            cleanOnceBeforeBuildPatterns: ['**/*', '!stats.json'],
        }),
        new CopyWebpackPlugin({
            patterns: [{
                from: path.resolve(environment.paths.source, 'site.webmanifest'),
                to: path.resolve(environment.paths.output, 'site.webmanifest'),
                toType: 'file',
                globOptions: {
                    ignore: ['*.DS_Store', 'Thumbs.db'],
                },
            }, {
                from: path.resolve(environment.paths.source, 'CNAME'),
                to: path.resolve(environment.paths.output, 'CNAME'),
                toType: 'file',
                globOptions: {
                    ignore: ['*.DS_Store', 'Thumbs.db'],
                },
            }, ],
        }),
    ].concat(htmlPluginEntries),
    target: 'web',
};

Sorry, it should be ejs-loader
image
image

@paulocoutinhox
Copy link

paulocoutinhox commented Mar 29, 2021

I changed index.html and partials/header.html to "ejs".

The file was included, but the images are not showed:
<%= require("./partials/header.ejs")() %>

image

partials/header.html
<img class="header-logo-image" src="../images/logo.png" alt="Logo">

And at last i need use "http://localhost:8000/index.ejs" and not "http://localhost:8000/index.html". There is any way to change it?

@paulocoutinhox
Copy link

It was published?

@alexander-akait
Copy link
Member

No, it is WIP

@ericalli
Copy link

Not 100% related but I used interpolate a lot to inline SVG files in HTML templates like this:

<div class="icon">
${require('!raw-loader!./images/logo.svg')}
</div>

I'm trying to figure out how to do this in the latest version after interpolate was removed.

Same here! Did you have any luck figuring this out?

@alexander-akait
Copy link
Member

In our roadmap, we are working on new release css-loader + style-loader/mini-css-extract-plugin, then we improve it

@xfg
Copy link

xfg commented May 10, 2021

Not 100% related but I used interpolate a lot to inline SVG files in HTML templates like this:

<div class="icon">
${require('!raw-loader!./images/logo.svg')}
</div>

I'm trying to figure out how to do this in the latest version after interpolate was removed.

My case too. I don't understand how to use webpack to embed SVG in HTML templates. Webpack documentation does not provide a clear answer.

@adyrose
Copy link

adyrose commented May 20, 2021

Just to chime in here, some people are not looking for "templating" as such. Sometimes i have projects which have tons of content and the HTML file ends up being huge and hard to maintain. This interpolate option really helped me "break" the content down into smaller manageable html chunks and was a really really nice feature. I purely used it for this functionality. I do not understand why, if you have a really good feature in your package which clearly lots of people are using and want back, youd shrug it off and tell them tough. Please consider reinstating it. Give the people what they want :D

@uncaught
Copy link

uncaught commented May 21, 2021

We heavily use this for other HTML files, SVGs and also css class names generated by the style-loader, e.g.:

<div class="${require('./style.scss').myClass}">
   ${require('./some.html')}
   ${require('./some.svg')}
</div>

So an upgrade for us is not possible without a replacement.

Will the <webpack-include/> support such css class names, too?

@AndreiLe
Copy link

Another option to replace interpolate
<%= require('html-loader?interpolate=true&minimize=false!./path-to-html.html') %>

Added directory and root path support #291 (comment)

Unfortunately html-loader and html-webpack-plugin are incompatible and lodash template in html-webpack-plugin <% =%> doesn't work.

index.html

<include src="./path-to-root-html.html"/>
<include src="path-to-html.html"/>

webpack.config.js

function processNestedHtml(content, loaderContext, resourcePath = "") {
  let fileDir = (resourcePath === "")? path.dirname(loaderContext.resourcePath) : path.dirname(resourcePath)
  const INCLUDE_PATTERN = /\<include src=\"(\.\/)?(.+)\"\/?\>(?:\<\/include\>)?/gi;

  function replaceHtml(match, pathRule, src) {
    if(pathRule === "./"){
      fileDir = loaderContext.context
    }
    const filePath = path.resolve(fileDir, src)
    loaderContext.dependency(filePath)
    const html = fs.readFileSync(filePath, 'utf8')
    return processNestedHtml(html, loaderContext, filePath)
  }

  if (!INCLUDE_PATTERN.test(content)) {
    return content
  } else {
    return content.replace(INCLUDE_PATTERN, replaceHtml);
  }
}

function processHtmlLoader(content, loaderContext){
    let newContent = processNestedHtml(content, loaderContext)
    return newContent
}
      {
        test: /\.html$/i,
        use: [
        {
          loader: 'html-loader',
          options: {
            sources: false,
            minimize: false,
            esModule: false,
            preprocessor: processHtmlLoader
          }
        }],
      },

@uvarov-frontend
Copy link

Делюсь своим временным решением поддержки style url():
@ - alias "/src" directory

{
  loader: 'html-loader',
  options: {
    sources: {
      list: [
        {
          attribute: "src",
          type: "src",
        },
        {
          attribute: "data-src",
          type: "src",
        },
      ],
    },
    preprocessor: (content) => content.replace(/\url\('~@\/[^)]+'\)/g,(match) => {
      let url = '';
      match.replace(/\'~@\/[^)]+'/,(path) => url = path.replace(/\'/g,''));
      return `${match.replace(/~@/, '')}" data-src="${url}`;
    }),
  },
},

Создаем у тега атрибут "data-src" с url background-image.
<div style="background-image: url('~@/img/sass-logo.png')">

На выходе:
<div style="background-image: url('/img/sass-logo.png')" data-src="/img/sass-logo.png">

Таким образом изображение обрабатывается webpack. Уже можно было бы закончить, но если вас как и меня не устраивает лишний data атрибут, то можно воспользоваться плагином html-replace-webpack-plugin:

new HtmlReplaceWebpackPlugin([
    {
      pattern: / data-src="[^"]+"/g,
      replacement: ''
    },
 ])

Итог:
<div style="background-image: url('/img/sass-logo.png')">

Этот вариант не будет работать с изображениями которые имеют хеш в имени.
Я знаю, этот вариант ужасный, но я решил поделиться с теми кому нужен хоть какой-то вариант поддержки style url.

@chkpnt
Copy link

chkpnt commented Oct 18, 2021

Not 100% related but I used interpolate a lot to inline SVG files in HTML templates like this:

<div class="icon">
${require('!raw-loader!./images/logo.svg')}
</div>

I'm trying to figure out how to do this in the latest version after interpolate was removed.

My case too. I don't understand how to use webpack to embed SVG in HTML templates. Webpack documentation does not provide a clear answer.

I'm currently using something like this:

                    {
                        loader: "html-loader",
                        options: {
                            esModule: false,
                            minimize: false,
                            preprocessor: (content, loaderContext) => {
                                var replaceWithSvg = function (arg1, arg2) {
                                    return fs.readFileSync("./src/_svg/" + arg2, "utf8", function (err, data) {
                                        if (err) {
                                            return "";
                                        }

                                        return data.fulltrim();
                                    });
                                };
                                content = content.replace(/<!-- @insertSvg (.*) -->/g, replaceWithSvg);
                                return content;
                            },
                            sources: false,
                        },
                    },

Unfortunately, using require(...) within the preprocessor doesn't work.

@xfg
Copy link

xfg commented Oct 21, 2021

Not 100% related but I used interpolate a lot to inline SVG files in HTML templates like this:

<div class="icon">
${require('!raw-loader!./images/logo.svg')}
</div>

I'm trying to figure out how to do this in the latest version after interpolate was removed.

My case too. I don't understand how to use webpack to embed SVG in HTML templates. Webpack documentation does not provide a clear answer.

I'm currently using something like this:

                    {
                        loader: "html-loader",
                        options: {
                            esModule: false,
                            minimize: false,
                            preprocessor: (content, loaderContext) => {
                                var replaceWithSvg = function (arg1, arg2) {
                                    return fs.readFileSync("./src/_svg/" + arg2, "utf8", function (err, data) {
                                        if (err) {
                                            return "";
                                        }

                                        return data.fulltrim();
                                    });
                                };
                                content = content.replace(/<!-- @insertSvg (.*) -->/g, replaceWithSvg);
                                return content;
                            },
                            sources: false,
                        },
                    },

Unfortunately, using require(...) within the preprocessor doesn't work.

Thanks, I got the idea. I'll try as the time will be. It is better than inserting svg content manually into the code.

@peimansh
Copy link

peimansh commented Mar 1, 2022

Our case is using Aliases for easier management of html files.
we use something like:

include '${require('@someComponent/xxx/xxxx/index.html')}'

in dozens of our html files which is kinda interpolating and was working fine until 0.5.5. Is there any easy workaround to keep this structure in our code without the need to modify hundreds of lines in our html files ?

@moonspam
Copy link

moonspam commented Mar 3, 2022

Our case is using Aliases for easier management of html files. we use something like:

include '${require('@someComponent/xxx/xxxx/index.html')}'

in dozens of our html files which is kinda interpolating and was working fine until 0.5.5. Is there any easy workaround to keep this structure in our code without the need to modify hundreds of lines in our html files ?

Try this

<%= require('html-loader!./partial.html').default %>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests