Code Monkey home page Code Monkey logo

webdiscus / html-bundler-webpack-plugin Goto Github PK

View Code? Open in Web Editor NEW
128.0 7.0 14.0 58.45 MB

Renders Eta, EJS, Handlebars, Nunjucks, Pug, Twig templates "out of the box". Uses HTML template as entry point. Resolves source files of scripts, styles, images in HTML. Supports for Vue.

License: ISC License

JavaScript 64.50% HTML 26.24% CSS 2.84% SCSS 2.01% Handlebars 0.96% EJS 0.09% Mustache 0.01% Liquid 0.03% PHP 0.06% Nunjucks 0.03% TypeScript 0.21% FreeMarker 0.05% Vue 0.08% Twig 0.09% Pug 2.79% C 0.01%
handlebars nunjucks template css html webpack bundler ejs javascript style

html-bundler-webpack-plugin's People

Contributors

paillat-dev avatar pirang avatar snyk-bot avatar steffenblake avatar striffly avatar webdiscus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

html-bundler-webpack-plugin's Issues

Live reloading of HTML partials does not work if JS is not used

Current behaviour

If I specify entry: 'src/views/' inside config, when I edit HTML partials, they are not updated on browser.
I have to edit the main template (index.html) to see the changes.

Reproduction Example

Environment

  • OS: Linux
  • version of Node.js: v18.17.1
  • version of Webpack: 5.88.2
  • version of the Plugin: 2.15.0

Favicon plugin causes a crash if no link is included

Current behaviour

If the HTML file does not include a <link rel="icon"> tag and the FaviconBundlerPlugin is enabled, webpack crashes with the following traceback:

  throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
        ^

Error: Tap function (tapPromise) did not return promise (returned undefined)
    at _next0 (eval at create (/home/ginger/vyxal.github.io/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:33:9)
    at eval (eval at create (/home/ginger/vyxal.github.io/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Expected behaviour

The plugin should probably throw an error explaining that the file lacks a link tag.

Reproduction Example

Create an HTML file without a <link rel="icon"> tag, enable the FaviconBundlerPlugin, and attempt to bundle it with the HtmlBundlerWebpackPlugin.

Environment

  • OS: Linux
  • version of Node.js: v18.13.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.0

In development mode, js references are lost in html and pathData.filename is lost in function when js.filename is a function after the project hot update

Current behaviour

As the title says.

Expected behaviour

Js references are not lost in html and pathData.filename is not lost in function.

Reproduction Example

You can just use the example 'simple-site', in order to keep the folder structure, I wrote a function:

const { relative, resolve, extname } = require('path');

const genNewName = (filename) => {
  console.log('filename: ', filename);
  return relative(
    resolve(__dirname, 'src'),
    filename.slice(0, filename.length - extname(filename).length)
  );
};

This can generate a new name, use it like this:

plugins: [
  new HtmlBundlerPlugin({
    // path to templates
    entry: 'src/views/',
    js: {
      // output filename of compiled JavaScript
      // filename: 'js/[name].[contenthash:8].js',
      filename: function ({ filename }) {
        const newName = genNewName(filename);
        return `${newName}.[contenthash:8].js`;
      },
    },
    css: {
      // output filename of extracted CSS
      filename: 'css/[name].[contenthash:8].css',
    },
  }),
],

It works when npm run build and the 1st time npm run start, but when you modify a style in scss file, it will not work, the loss as title says will happen.

屏幕截图 2023-12-31 221427

屏幕截图 2023-12-31 221746

Environment

  • OS: [macOS, Linux, Windows] Windows
  • version of Node.js: 18.18.2
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.4

Additional context

None.

TypeScript: `exports` field in `package.json` missing a `types` field (required even if there is already a top level "types" field)

Current behaviour

image

importing html-bundler-webpack-plugin causes typescript error:

Could not find a declaration file for module 'html-bundler-webpack-plugin'. '/home/david/code/test/empty/node_modules/html-bundler-webpack-plugin/src/index.js' implicitly has an 'any' type.
Try npm i --save-dev @types/html-bundler-webpack-plugin if it exists or add a new declaration (.d.ts) file containing declare module 'html-bundler-webpack-plugin';ts(7016)

Expected behaviour

Types should work in latest typescript with "module": "node16" and "strict": true.

Here is some information about how typescript resolves types fields:
https://www.typescriptlang.org/docs/handbook/modules/reference.html#packagejson-typesversions

I think you need to add a "types" field to the "exports" in the package.json. Something like (but I'm not entirely sure this is 100% correct):

  "exports": {
-   ".": "./src/index.js",
+   ".": {
+     "require": "./src/index.js",
+     "import": "./src/index.js",
+     "types": "./types.d.ts"
+   },
    "./plugins": "./plugins/index.js",
    "./plugins/favicons-bundler-plugin": {
      "types": "./plugins/favicons-bundler-plugin/index.d.ts",
      "require": "./plugins/favicons-bundler-plugin/index.js",
      "import": "./plugins/favicons-bundler-plugin/index.js"
    }
  },

Reproduction Example

Create the 3 files below then run yarn && npx -y tsc.

// package.json
{
  "private": true,
  "devDependencies": {
    "html-bundler-webpack-plugin": "^3.5.1",
    "typescript": "^5.3.3",
    "webpack": "^5.90.3"
  }
}
//webpack.config.ts
import HtmlBundlerPlugin from 'html-bundler-webpack-plugin';

export default {
    plugins: [
        new HtmlBundlerPlugin()
    ]
}
// tsconfig.json
{
    "compilerOptions": {
      "module": "node16",
      "strict": true
    }
  }

Environment

  • OS: Ubuntu
  • version of Node.js: 20.11.0
  • version of Webpack: 5.90.3
  • version of the Plugin: 3.5.1

Additional context

n/a

Setting Root Public Path with more than 1 character breaks Integrity

Current behavior

Integrity setting now works for a public path of /, but adding anything beyond that breaks it again producing the same errors as #42 and #43 (IE if your public path is /test

Expected behavior

Complex Root Public Path should work with integrity

Reproduction Example

https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/main/webpack.config.js#L32-L33

npm run build with current settings works and produces the expected output.

Switching the commented out publicPath config to be /test reproduces the bug.

Environment

  • OS: Windows
  • version of Node.js: v21.1.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 2.15.1

Proposed Fix

Forking a branch, will be opening a PR shortly.

Resolve custom attributes, wrong type of `value` argument in filter()

The another edge.

<a
    href="../img.png?as=webp-xl"
    data-pswp-width="990"
    data-pswp-height="1320"
    data-pswp-srcset="../img.png?as=webp-xs 540w, ../img.png?as=webp-sm 720w, ../img.png?as=webp-md 960w, ../img.png?as=webp-lg 1140w, ../img.png?as=webp-xl 1320w"
    data-cropped="true"
    target="_blank"
><img class="card-img-top" src="../img.png?as=webp-xs" alt="..."></a>

The HTML markup for Photoswipe.js. The data-pswp-srcset is simular to srcset.
The queries are processed by the custom generators in image-minimizer-webpack-plugin

The plugin configuration:

...
loaderOptions: {
    sources: [
      {
        tag: 'a',
        attributes: ['href'],
        filter({ value }) {
          const path = value.split('?')[0];
          const imgRegex = new RegExp('\\.(avif|gif|heif|jp[2x]|j2[kc]|jpe?g|jpe|jxl|png|raw|tiff?|webp|svg)(?:[\\W].*)?$', 'i');

          return imgRegex.test(path)
        }
      },
      {
        tag: 'a',
        attributes: ['data-pswp-srcset'],
      },
    ],
  },
...

Now I have error: split is not a function;
I'm surprised...

New config:

...
loaderOptions: {
    sources: [
      {
        tag: 'a',
        attributes: ['href'],
        filter({ value }) {
          if (typeof(value) !== 'string') {
            console.log(typeof (value), value);

            return false;
          }
          const path = value.split('?')[0];
          const imgRegex = new RegExp('\\.(avif|gif|heif|jp[2x]|j2[kc]|jpe?g|jpe|jxl|png|raw|tiff?|webp|svg)([?:\\W].*)?$', 'i');

          return imgRegex.test(path)
        }
      },
      {
        tag: 'a',
        attributes: ['data-pswp-srcset'],
      },
    ],
  },
...

Terminal:

...
object [
  '../img.png?as=webp-xs',
  '../img.png?as=webp-sm',
  '../img.png?as=webp-md ',
  '../img.png?as=webp-lg',
  '../img.png?as=webp-xl'
]
...

Object is data-pswp-srcset And now it lost the width values. I'm realy surprised...

Originally posted by @vralle in #34 (comment)

Inline not working with minify

Current behaviour

When enabling minify, style and scripts are not inlined properly

Expected behaviour

style and scripts are inlined properly with minify on

Reproduction Example

new HtmlBundlerPlugin({
  minify: true,
  js: {
    inline: true
  },
  css: {
    inline: true
  }
})

Page:

<script src="./script.js"></script>
<link rel="stylesheet" href="./style.css" />

Output:
Script contains the path, style is empty.

<script>path,to,my,script.js</script>
<style></style>

If I disable minify, it inlines correctly.

Environment

  • OS: macOS
  • version of Node.js: 19.6.1
  • version of Webpack: 5.78.0
  • version of the Plugin: 1.15.0

Additional context

Commented out markup causes files to be included

Current behaviour

Commented out markup on .ejs (and potential .html files by extension) are parsed as if they are not commented out.

Expected behaviour

Commented out markup should be ignored by the bundler for treeshaking, and should not cause script/style references to be processed and compiled if they are inside of comments.

Reproduction Example

https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/29131635a37f9be1bd0e9187734d609d3178da99/src/views/home.ejs#L4

If you comment out the line like so:

<!-- <script src="./home.js" defer="defer"></script> -->

The Bundler will still pickup the js file and parse it and produce it as an output in the /dist folder.

Environment

  • OS: Windows
  • version of Node.js: v21.1.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 2.15.0

Allow to pass minify options and set to auto

Feature request

What is motivation or use case for adding/changing the behavior?

Minification is only desired when in production mode, and I would like to customize the default minification options.

As far as I can tell, there is no way to pass an object of custom objects and keep minification to auto/when only in production.

See: https://github.com/webdiscus/html-bundler-webpack-plugin#minify

Describe the solution you'd like

Somehow combine false, true and auto with the configuration object.

How to use tailwindcss globally with HtmlBundlerPlugin?

I'm using HtmlBundlerPlugin to bundle my HTML files in a webpack project. I want to use tailwindcss globally in my project, but I'm having trouble configuring it.

I've tried importing tailwindcss in my global main.scss file using @use 'tailwindcss/index.css';, but it doesn't seem to be parsed correctly. The styles are not applied to my HTML files.
image

The only way I've been able to make it work is by adding a link to the tailwindcss index.css file in my default.html layout file using <link rel="stylesheet" href="@styles/tailwindcss/index.css">.

Is there a way to configure HtmlBundlerPlugin to use tailwindcss globally without having to add the link to every HTML file? Any help would be appreciated.

Thanks!

How to resolve an asset path when using HTML template in JavaScript

Current behaviour

The relative paths of src files in HTML templates cannot be resolved correctly in JavaScript.

const aiHtml = `
<img
  class="w-7 h-7 sm:w-10 sm:h-10 rounded-full mr-2 sm:mr-4 shrink-0"
  src="../../../assets/images/chat-ai.png"
  alt="" />
`;

// or
const aiHtml = `
<img
  class="w-7 h-7 sm:w-10 sm:h-10 rounded-full mr-2 sm:mr-4 shrink-0"
  src="@images/chat-ai.png"
  alt="" />
`;

Environment

  • OS: Windows
  • version of Node.js: 18.16.0
  • version of Webpack: 5.87.0
  • version of the Plugin: 1.18.0

new eta template function only works if some data is specified in loaderOptions

Current behaviour

If you create a partial eta template and import it into js without configuring any data in loaderOptions, an exception is thrown because __data__ is null, and the template doesn't work.

Uncaught TypeError: can't convert null to object
    templateFn file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:30
    updatePage file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:131
    <anonymous> file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:126
    EventListener.handleEvent* file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:125
    <anonymous> file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:134
    <anonymous> file://///wsl$/Debian/home/kiirani/code/tmp/hbwp-template-repro/dist/app.js:136

Expected behaviour

The template should work without requiring developers to pass data in through the preprocessor config.

Reproduction Example

https://github.com/Kiirani/hbwp-template-repro

Environment

  • OS: WSL, debian
  • version of Node.js: 21.4.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.0

Additional context

The test case for this feature (https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/test/cases/js-tmpl-eta-compile) tests multiple methods of passing data, including through through the config - that's actually how I figured out why it wasn't working....

... possibly splitting that case into separate tests for each data passing mechanism would have caught this? I'm not very familiar with the tooling involved here.

it also might be worth looking at whether this is a problem with the other templating engines

installation error caused from installing favicon

i cant install the versions with built-in favicon plugin its giving me this error

`npm ERR! code EINVALIDTAGNAME
npm ERR! Invalid tag name "^>=7.1.4" of package "favicons@^>=7.1.4": Tags may not have any characters that encodeURIComponent encodes.

npm ERR! A complete log of this run can be found in: /Users/hamzaduras/.npm/_logs/2023-11-13T13_51_10_276Z-debug-0.log`

  • Macos sanoma 14.1.1
  • version of Node.js: 21.1.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.0.1 & 3.0.0

Helper embed / content

Feature request

What is motivation or use case for adding/changing the behavior?

Latest build in helpers assign, partial, block are sufficient in most situations. Though there are situations where embed or content helper has more power about controling the block content.
Imagine you have section partial

<section>
    <h2>{{title}}</h2>
    {{#block 'section-content'}}
    Default content
    {{/block}}
</section>

Now if I assign custom content to the first section

{{#partial 'section-content'}}
Hello World
{{/partial}}
{{>section title="Lorem"}}
{{>section title="Ipsum"}}
{{#partial 'section-content'}}
Foo Bar
{{/partial}}
{{>section title="Dolor"}}

The custom content will be applied to the second section too instead of leaving the default value.

Describe the solution you'd like

In the past I used handlebars-layouts (https://www.npmjs.com/package/handlebars-layouts) to extend my views which you have done with partial helper (great!). But there is also a embed and content helper which was used in this format:

{{#embed "section" title="Lorem"}}
{{#content 'section-content' mode='(append|prepend|replace)'}}
Hello wordl
{{/content}}
{{/embed}}

This way the custom content was applied only to the specific partial but as you can see it will handle only one partial at a time.

Describe alternatives you've considered

I tried to reset the partial helper to empty string but that will remove the default content of the block.

{{#partial 'section-content'}}
Hello World
{{/partial}}
{{>section title="Lorem"}}
{{#partial 'section-content'}}{{/partial}}
{{>section title="Ipsum"}}
{{#partial 'section-content'}}
Foo Bar
{{/partial}}
{{>section title="Dolor"}}

Maybe some reset helper would be sufficient (?)

Access @root in json

Current behaviour

In Handlebars when I am inside iteration loop I am not able to access @root of the json data. For example inside loop of foo if I want access {{bar}} I should do it with {{@root.bar}} or less comfortable and traversing upwards {{../bar}}. Traversing is usable but loop inside loop ... will look like {{../../bar}} and also this is not usable if I want to use the bar inside partial inside loop..

Expected behaviour

Calling @root.bar should output the value of bar in root of the data.json

Reproduction Example

Basic data.json:

{
    "foo": [
        "a", "b", "c"
    ],
    "bar": [
        "alfa", "beta"
    ]
}

Simple loop

{{#each foo}}
    {{this}}
    {{#each @root.bar}}
        {{this}}
    {{/each}}
{{/each}}

nor

{{#each foo}}
    {{this}}
    {{@root.bar}} {{!-- outputs nothing --}}
{{/each}}

Environment

  • OS: macOS
  • version of Node.js: v18.16.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.0.3

Additional context

This behaviour was on this plugin v2.* too.
@data variables at handlebars documentation: https://handlebarsjs.com/api-reference/data-variables.html

html template inline scripts import statement fetching another script don't seem to resolve in output html files

It appears that if I have inline script such as shown below in my html template file, then the output html file still has the path ../scripts/someScript.js instead of changing this to HtmlBundlerWebpackPlugin js.filename that is changing the path that would point to output asset js files.

Does HtmlBundlerWebpackPlugin support resolving these kind of import statements in html templates?

<script type="module">
    import { someMethod } from '../scripts/someScript.js';
</script>

[FEATURE REQUEST] Watch handlebars helpers for changes

Current behaviour

Changes made to handlebars helper *.js are not compiled. Page reloads on helper save, but the changes are not compmilet back to the output. As for now the solution is to restart npm run start. Also adding/renaming/deleting helper file does not made any changes.

Expected behaviour

Changes should be compiled to the code.

Reproduction Example

loaderOptions: {
    preprocessor: 'handlebars',
    preprocessorOptions: {
        root: path.join(PATH_PAGES),
        views: [
            PATH_PARTIALS
        ],
        partials: [
            PATH_PARTIALS
        ],
        helpers: [
            PATH_HELPERS
        ]
    },
},

helpers are in project root helpers/*.js

Environment

  • OS: [macOS, Linux, Windows]
  • version of Node.js: v18.16.0
  • version of Webpack: 5.88.2
  • version of the Plugin: 2.7.0

Additional context

Thank you

Load partials files only with handlebars's file extensions

Feature request

In loaderOptions would be great to set for each preprocessorOptions to accept/ignore only specific extensions.

What is motivation or use case for adding/changing the behavior?

Imagine partials tree like this:

partials/
├─ card/
│  ├─ card.hbs
│  ├─ card.scss
├─ hero/
│  ├─ hero.hbs
│  ├─ hero.scss

As you can see .hbs files are organised next to its .scss file. Now if I include partials the native partials way like {{>card/card}} it renders the scss file instead of the hbs. If I rename the scss file to card1.scss, the hbs file is embedded correctly.
If I include partials with {{include 'card/card'}}, the hbs is included correctly.

Describe the solution you'd like

If the preprocessorOptions is limited for example to include only .hbs files, then would work also the classic partials {{>card/card}} include.

Describe alternatives you've considered

The only alternative is to use {{include 'card/card'}} which surprisingly works and ignores the .scss file.

Edit: {{include ... }} cannot be used inside loops as it wont passtrough data

[FEATURE REQUEST] Allow to use the `/` as root path to source files

Current behaviour

The CSS, JS and images in my bundled HTML files aren't being output to my output folder, dist/

Expected behaviour

CSS and JS files refrenced in bundled HTML files should be output to the dist/ folder

Reproduction Example

My folder structure:

├── css
│   ├── _builders-challenge.scss
│   ├── _cohort.scss
│   ├── _footer.scss
│   ├── _innovation-landing.scss
│   ├── _ticker.scss
│   └── style.scss
├── html_output
│   ├── builders-challenge
│   │   └── index.html
│   ├── index.html
│   └── mieco
│       └── index.html
├── scripts
│   ├── analytics.js
│   ├── email.js
│   └── form-utils.js
└── webpack.config.js

My webpack config:

module.exports = {
  output: {
    path: path.resolve(__dirname, "dist"),
    publicPath: "/",
  },
  plugins: [
    new HtmlBundlerPlugin({
      entry: "./html_output/",
      js: {
        // output filename of extracted JS from source script loaded in HTML via `<script>` tag
        filename: "[name].[contenthash:8].js",
        verbose: true,
      },
      css: {
        // output filename of extracted CSS from source style loaded in HTML via `<link>` tag
        filename: "css/[name].[contenthash:8].css",
        verbose: true,
      },
    }),

Environment

  • OS: macOS Ventura 13.1
  • version of Node.js: 14.17.6
  • version of Webpack: 5.75
  • version of the Plugin: 1.12

Additional context

[FEATURE REQUEST] handlebars data accept a json file

Feature request

What is motivation or use case for adding/changing the behavior?

I'm migrating from HandlebarsPlugin to HtmlBundlerPlugin and I can't figure out how to pass data from data.json file to data entry. data accepts only object | null and does not accept path to json.

Describe the solution you'd like

Simple:

data: path.join(PATH_SRC, 'data.json'),

Describe alternatives you've considered

ATM the only alternative is to stick with webpack-handlebars-plugin where the configuration looks like this:

new HandlebarsPlugin({
    entry: path.join(PATH_PAGES, '!(assets|data|libs|hbs_helpers|layout|partials){,**/}*.hbs'),
    output: path.join(PATH_DIST, '[path]', '[name].html'), // is rewritten in getTargetFilepath
    data: path.join(PATH_SRC, 'data.json'),
    partials: [
        path.join(PATH_LAYOUT, '*.hbs'),
        path.resolve(PATH_PARTIALS, '*', '*.hbs')
    ],
    ...
}),

file data.json is also watched an works fine with HMR
My current try with HtmlBundlerPlugin is this:
data.json

{
    "foo": "bar"
}

webpack.config.js

new HtmlBundlerPlugin({
    entry: path.join(PATH_PAGES),
    loaderOptions: {
        preprocessor: 'handlebars', // enable Handlebars compiler
        data: path.join(PATH_SRC, 'data.json'), // {{foo}} returns nothing
        // data: {
        //     title: 'meh'
        // }, // {{title}} returns 'meh'
        preprocessorOptions: {},
    },
}),

Exclude dir from minifing

Feature request

What is motivation or use case for adding/changing the behavior?

Included 3rd party libs (js, css) are minified already or one wont minify them at all, but there's no way to exclude dir containing files like that eg. /libs. Maybe this is already implemented and I am blind or incapable.

Describe the solution you'd like

Have an option to exclude directory from minifyng and resolving paths inside them, just keep watching files for changes.

Describe alternatives you've considered

Right now I use CopyPlugin to copy libs to it's place on build but every file included in src or link is compiled and at the end replaced. Used it together with TerserPlugin but somehow it is ignored or overriden with bundler plugin.

Respect realContentHash option and calculate real contenthash

Current behaviour

Plugin does not respect realContentHash: true.
[contenthash] in filename is always wrong, because of minimization and optimization is called after assets declaration.

Expected behaviour

[contenthash] should be md4 of css / js output, and should be calculated at last optimization step.

Reproduction Example

Add optimization: { realContentHash: true }, build something, and check real hash of file.

Environment

  • OS: macOS
  • version of Node.js: 16.10
  • version of Webpack: 5.82.1
  • version of the Plugin: 1.18.0

Additional context

This is important for artefacts testing.
I fixed it by custom plugin, that calculates hash at very last step, but this is too hackish.
It will be much better if real contenthash will be added to plugin by default

compiler.hooks.compilation.tap('ApplyHTMLBundlerHashFixPlugin', (compilation) => {
  compilation.hooks.processAssets.tapPromise({ name: `html-bundler-webpack-plugin`, stage: Infinity }, () => {
    for (assetName in compilation.assets) {
      if (assetName.endsWith('.css') || assetName.endsWith('.js')) {
        const asset = compilation.getAsset(assetName);
        const source = asset.source.source();
        const hash = webpack.util.createHash('md4')
        hash.update(source);
        const digest = hash.digest('hex').slice(0, 20);
        const newAssetName = assetName.replace(/(\.[a-z0-9]{20})\./, `.${digest}.`);
        compilation.renameAsset(assetName, newAssetName);
        compilation.updateAsset(newAssetName, asset.source, {
          ...asset.info,
          contenthash: digest,
        });
      }
    }

    return Promise.resolve();
  });
});

Disable url resolving

Let's consider that in the html entry file there are some resources that should not be resolved by webpack - these resources will be placed in the public folder and copied in the dist folder with webpack-copy-plugin.

To be more clear, the behaviour is similar with the html-loader feature - https://github.com/webpack-contrib/html-loader#examples.

Is there any built-in option in this plugin?

Thank you!

Watching for create/rename .hbs files with HMR is not fully working

Current behaviour

I have suspicion that there's something wrong with files watching (HMR module) or something similiar. Imagine this situation:

You run npm run start to watch files for changes (HMR enabled), then include existing partial {{>foo}}, then create a new partial called bar.hbs and include it inside index file as follows {{>bar}}. You will get an error:

ERROR in ./src/views/pages/index.hbs
Module Error (from ./node_modules/html-bundler-webpack-plugin/src/Loader/index.js):

 html-bundler-webpack-plugin  Preprocessor failed.
File: src/views/pages/index.hbs
Error: The partial bar could not be found
    at Object.invokePartial (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/runtime.js:332:11)
    at Object.invokePartialWrapper [as invokePartial] (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/runtime.js:84:39)
    at Object.eval [as main] (eval at createFunctionContext (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js:262:23), <anonymous>:16:28)
    at main (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/runtime.js:208:32)
    at ret (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/runtime.js:212:12)
    at ret (/Users/e/http/devel/test-bundler/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:519:21)
    at render (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Loader/Preprocessors/Handlebars/index.js:102:93)
    at Promise.then.errorStage (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Loader/index.js:54:16)
    at new Promise (<anonymous>)
    at Object.loader (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Loader/index.js:43:3)

So you have to stop the npm and run npm run start again.. then the new bar.hbs is recognised. Now let's say you need to rename the bar.hbs to lorem.hbs because of for whatever reason. So you change {{>bar}} to {{>lorem}} in index file, get an error (which was expected as the file lorem.hbs does not yet exist), then you rename bar.hbs to lorem.hbs and you get a huuuge red error in terminal:

webpack 5.84.1 compiled with 1 error in 32 ms
<e> [webpack-dev-middleware] Error: ENOENT: no such file or directory, open '/Users/e/http/devel/test-bundler/src/views/partials/bar.hbs'
<e>     at Object.openSync (node:fs:585:3)
<e>     at Object.readFileSync (node:fs:453:35)
<e>     at Object.updatePartials [as watch] (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Loader/Preprocessors/Handlebars/index.js:19:27)
<e>     at Function.watchRun (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Loader/Preprocessor.js:84:20)
<e>     at Function.watchRun (/Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Plugin/PluginService.js:60:20)
<e>     at /Users/e/http/devel/test-bundler/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:166:21
<e>     at Hook.eval [as callAsync] (eval at create (/Users/e/http/devel/test-bundler/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:18:1)
<e>     at run (/Users/e/http/devel/test-bundler/node_modules/webpack/lib/Watching.js:168:33)
<e>     at /Users/e/http/devel/test-bundler/node_modules/webpack/lib/Watching.js:155:6
<e>     at /Users/e/http/devel/test-bundler/node_modules/webpack/lib/HookWebpackError.js:68:3 {
<e>   errno: -2,
<e>   syscall: 'open',
<e>   code: 'ENOENT',
<e>   path: '/Users/e/http/devel/test-bundler/src/views/partials/bar.hbs'
<e> }

The same goes with pages (views).
But that's not all. Your partials are structured like this:

partials/
│- foo.hbs
│- bar.hbs
└─ blog/
  │- list.hbs
  └─ card.hbs

If you do some change inside card.hbs the page will be reloaded (so HMR is watchind) but no change will be visible. But if you update foo|bar.hbs file it will update also the card.hbs output.

Expected behaviour

I expect the .hbs would be watched for changes. So if I create new contact.hbs view file I can access it as /contact.html. If I create new partial or sub-partial, would be great to have the option to immediately inlcude it {{>blog/card}} without stopping npm. And also the sub-parts should be watched. This expected behaviour is working for example with HandlebarsPlugin.

Reproduction Example

  1. Run npm run start with enabled HMR
  2. inside index view include {{>foo}} partial.
  3. in filesystem rename foo.hbs to bar.hbs -> error
  4. create new partials lorem.hbs and include it in index {{>lorem}} -> error
  5. create new page contact.hbs and load it /contact.html -> error Cannot GET /contact

Environment

  • OS: macOS 13.4
  • version of Node.js: v18.16.0
  • version of Webpack: 5.84.1
  • version of the Plugin: 1.18.0

Additional context

Here's my webpack.config.js:

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = (env, argv) => {
    return {
        output: {
            path: path.join(process.cwd(), 'dist'),
        },
        devServer: {
            port: 3000,
            open: true,
            host: '0.0.0.0',
            static: {
                directory: path.join(__dirname, 'dist'),
            },
            watchFiles: {
                paths: ['src/**/*.*'],
                options: {
                    usePolling: true,
                },
            },
        },
        plugins: [
            new HtmlBundlerPlugin({
                entry: path.join(__dirname, 'src/views/pages'), // home, contact, blog...
                loaderOptions: {
                    data: path.join(process.cwd(), 'src/data.json'),
                    root: path.join(__dirname, 'src'),
                    preprocessor: 'handlebars',
                    preprocessorOptions: {
                        // root: path.join(__dirname, 'src/views/'),
                        views: [
                            'src/views/pages',
                        ],
                        partials: [
                            path.join(__dirname, 'src/views/partials'),
                        ],
                    },
                },
                js: {
                    filename: 'js/[name].[contenthash:8].js',
                },
                css: {
                    filename: 'css/[name].[contenthash:8].css',
                },
            }),
        ],
        module: {
            rules: [
                {
                    test: /\.(css|sass|scss)$/,
                    use: ['css-loader', 'sass-loader'],
                },
                {
                    test: /\.(ico|png|jp?g|svg)$/,
                    type: 'asset/resource',
                    generator: {
                        filename: 'img/[name].[hash:8][ext][query]',
                    },
                },
                {
                    test: /\.(html|hbs|handlebars)$/,
                    loader: HtmlBundlerPlugin.loader,
                }
            ],
        },
    }
};

And my dir structure:

.
├── dist
├── package-lock.json
├── package.json
├── src
│   ├── css
│   ├── data.json
│   ├── img
│   ├── js
│   └── views
│       ├── pages
│       │   ├── index.hbs
│       │   └── sub.hbs
│       └── partials
│           ├── foo.hbs
│           ├── lorem.hbs
│           └── blog
│               └── card.hbs
└── webpack.config.js

Strange Interaction Between This Plugin and MUI

Current behaviour

When I import any component from @mui, the build process appears to freeze. There is no output (to console or files), and when I waited quite some time for the build to progress it did not.

For example, this will build fine (takes ~3500 ms):

import { createRoot } from 'react-dom/client';
import './index.css';

const root = createRoot(document.getElementById('app')!);

root.render(
<div>
    <p>Hello!</p>
    <p>How are you?</p>
</div>
);

But this appears to freeze the plugin:

import { createRoot } from 'react-dom/client';
import './index.css';
import Stack from '@mui/material/Stack';

const root = createRoot(document.getElementById('app')!);

root.render(
<Stack>
    <p>Hello!</p>
    <p>How are you?</p>
</Stack>
);

Expected behaviour

My expectation is a successful build similar to the result when not using MUI (as described above).

Reproduction Example

I have a mini reproduction example here: https://github.com/DylanVause/mui-webpack-bug

Environment

  • OS: Windows 11
  • version of Node.js: v18.17.1
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.0

Additional context

My Webpack config is in the reproduction repository, but I will include it here as well for convenience:

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = [
    {
        name: 'client',
        mode: 'development',
        output: {
            path: path.resolve(__dirname, 'client-dist'),
            clean: true
        },
        resolve: {
            alias: {
                client: path.resolve(__dirname, 'client')
            },
            extensions: ['.js', '.ts']
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'ts-loader',
                            options: {
                                transpileOnly: true,
                            },
                        },
                    ],
                },
                {
                    test: /\.css$/,
                    use: 'css-loader',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                },
                {
                    test: /\.(ico|png|jp?g|svg)/,
                    type: 'asset/resource',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    generator: {
                        filename: 'img/[name].[hash:8][ext]'
                    }
                }
            ],
        },
        plugins: [
            new HtmlBundlerPlugin({
                entry: 'client/',
                js: {
                    filename: 'js/[name].[contenthash:8].js'
                },
                css: {
                    filename: 'css/[name].[contenthash:8].css'
                },
            })
        ],
        optimization: {
            usedExports: true,
        }
    },
];

By the way I absolutely love this plugin and apologize in advance if this 'bug' is just a result of me not knowing what I am doing.

Hooks / Customize output dir structure

Feature request

What is motivation or use case for adding/changing the behavior?
Sorry for spamming and comparing the functionality with HandlebarsPlugin :) At this moment we can define pages inside webpack.config

entry: {
    index: 'src/views/home/index.html', // => dist/index.html
    'news/sport': 'src/views/news/sport/index.html', // => dist/news/sport.html
},

or dynamically:

entry: path.join(__dirname, 'src/views/pages'), // => dist/subdirname/filename.html

But what if you have one folder pages like this:

.
├── foo
│   └── bar.hbs
├── foo.hbs
├── blog.hbs
├── index.hbs
└── sub.hbs

and the desired output is

/index.html
/foo/bar/index.html
/foo/index.html
/blog/index.html
/sub/index.html

Yes, you can do that manually just in the file structure but if you have open multiple files in e.g. VSCode editor, you see in tabs only filenames like index.html, index.html, index.html.. that's not much friendly.

Describe the solution you'd like

So how about some hooks like in Handlebars plugin where you can do whatever you want?

      // hooks
      // getTargetFilepath: function (filepath, outputTemplate) {},
      // getPartialId: function (filePath) {}
      onBeforeSetup: function (Handlebars) {},
      onBeforeAddPartials: function (Handlebars, partialsMap) {},
      onBeforeCompile: function (Handlebars, templateContent) {},
      onBeforeRender: function (Handlebars, data, filename) {},
      onBeforeSave: function (Handlebars, resultHtml, filename) {},
      onDone: function (Handlebars, filename) {}

Or having the possibility to customize output for entry via function.
For imagination here's my old config where I did the same I described before:

new HandlebarsPlugin({
    entry: './src/html/!(assets|data|libs|helpers|partials){,**/}*.hbs',
    output: path.join(dirDist, '[path]', '[name].html'), // is rewritten in getTargetFilepath
    data: path.join(dirSrc, 'data.json'),
    partials: [
        path.join(dirLayout, '*.hbs'),
        path.resolve(dirPartials, '*', '*.hbs')
    ],
    getTargetFilepath: function getTargetFilepath(filepath, outputTemplate, rootFolder) {
        const sanitizePath = require('handlebars-webpack-plugin/utils/sanitizePath');
        filePath = sanitizePath(filepath);
        rootPath = rootFolder ? sanitizePath(rootFolder) : path.dirname(filePath);
        if (outputTemplate == null) {
            return filePath.replace(path.extname(filePath), "");
        }
        
        const folderPath = path
        .dirname(filePath)
        .split(rootPath)[1];
        
        const fileName = path
        .basename(filePath)
        .replace(path.extname(filePath), "");
        
        const page = fileName !== 'index' ? '/'+fileName.replace(" ", "-") : '';
        if (niceUrl) {
            return outputTemplate
                .replace("[path]", folderPath+page)
                .replace("[name]", 'index');
        } else {
            return outputTemplate
                .replace("[path]", folderPath)
                .replace("[name]", fileName);
        }
    },
    helpers: {
        projectHelpers: path.join(dirHelpers, "*.js"),
        wroot: function () { // webroot helper for relative path
            return '{{wroot}}';
        }
    },
    onBeforeSetup: (Handlebars) => {
        return registerHandlersHelpers(Handlebars);
    },
    onBeforeRender: (Handlebars, data) => {
        return makeDataReplacements(data);
    },
    onBeforeSave: function (Handlebars, resultHtml, filename) {
        const level = filename.split('//').pop().split('/').length;
        function relPath(level) {
            var res = '.';
            for (var i = 0; i < level; i++) {
                if ( i > 0 ) {
                    res += '/..';
                }
            }
            return res;
        }
        const finalHtml = resultHtml.split('{{wroot}}').join(relPath(level));

        return finalHtml;
    },
    onDone: function (Handlebars, filename) {}
}),

Describe alternatives you've considered

Atm the only alternative is the manual way mentioned before.

How to disable resolving of empty srcset attribute

Description: When using an empty srcset attribute in an image tag, such as , the HtmlBundlerPlugin fails to resolve the file and throws a LoaderException. The error message reads: "html-bundler-webpack-plugin The file '' can't be resolved in the template src\views\pages\home\index.html".

Expected behavior: The HtmlBundlerPlugin should be able to handle empty srcset attributes and not throw an error.

Steps to reproduce:

  • Add an image tag with an empty srcset attribute to an HTML file.
  • Run the HtmlBundlerPlugin.
  • Check the console for errors.
    Possible solution: The HtmlBundlerPlugin could be updated to handle empty srcset attributes and not throw an error.

Not All Expected CSS Files Were Generated

Current behaviour

I build a React component Foo with Foo.css for styles. I use Foo in Page A and Page B, which are two near-identical HTML pages powered by React. Oddly, Foo is only styled in either Page A or Page B, but not both. Note that the same issue apples to component Bar.

Foo.tsx:

import { ReactElement } from "react";
import "./Foo.css";

export default function Foo(p: { children?: string | ReactElement | ReactElement[] }) {
    return (<div className="foo">
        {p.children}
    </div>
    );
}

Foo.css:

.foo {
    color: rgb(255, 158, 30);
    background-color: white;
    border: 1px solid rgb(255, 158, 30);
    border-radius: 5px;
}

pageA.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Page A</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="pageA.tsx"></script>
    </body>
</html>

pageB.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Page B</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="pageB.tsx"></script>
    </body>
</html>

pageA.tsx (pageB.tsx is similar):

import { createRoot } from 'react-dom/client';
import Foo from '../components/Bar';
import Bar from '../components/Foo'; 

const root = createRoot(document.getElementById('app')!);

root.render(
    <div style={{display: 'flex', flexDirection: 'column'}}>
        <p>Page A</p>
        <a href='/pages/pageB.html'>Visit Page B</a>
        <b>Styled components:</b>
        <Foo>Foo</Foo>
        <Bar>Bar</Bar>
    </div>
);

For some reason, in the output directory, only one of pageA.{contenthash}.css or pageB.{contenthash}.css is generated, but not both. Expected behaviour is that both would be generated.

The generated HTML files look like this:
pageA.html (generated):

<!DOCTYPE html>
<html>
    <head>
        <title>Page A</title>
    <!-- 
        Where is <link href="../css/pageA.{contenthash}.css" rel="stylesheet">? 
    -->
    </head>
    <body>
        <div id="app"></div>
        <script src="../js/pageA.f6d77860.js"></script>
    </body>
</html>

pageB.html (generated):

<!DOCTYPE html>
<html>
    <head>
        <title>Page B</title>
    <link href="../css/pageB.e7abdc32.css" rel="stylesheet">
</head>
    <body>
        <div id="app"></div>
        <script src="../js/pageB.ef1af23d.js"></script>
    </body>
</html>

Expected behaviour

Foo should be styled in both Page A and Page B. Both pageA.{contenthash}.css andpageB.{contenthash}.css should be generated and included in pageA.html and pageB.html.

Reproduction Example

I have a mini reproduction example here: https://github.com/DylanVause/webpack-multiple-css-imports-bug

Environment

  • OS: Windows 11
  • version of Node.js: v18.17.1
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.6

Additional context

My Webpack config is in the reproduction repository, but I will include it here as well for convenience:

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = [
    {
        name: 'client',
        mode: 'development',
        output: {
            path: path.resolve(__dirname, 'client-dist'),
            clean: true
        },
        resolve: {
            alias: {
                client: path.resolve(__dirname, 'client')
            },
            extensions: ['.js', '.ts', '.tsx']
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'ts-loader',
                            options: {
                                transpileOnly: true,
                            },
                        },
                    ],
                },
                {
                    test: /\.css$/,
                    use: 'css-loader',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                },
                {
                    test: /\.(ico|png|jp?g|svg)/,
                    type: 'asset/resource',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    generator: {
                        filename: 'img/[name].[hash:8][ext]'
                    }
                },
            ],
        },
        plugins: [
            new HtmlBundlerPlugin({
                entry: 'client/',
                js: {
                    filename: 'js/[name].[contenthash:8].js'
                },
                css: {
                    filename: 'css/[name].[contenthash:8].css'
                },
            })
        ],
        optimization: {
            usedExports: true,
        }
    },
];

Thank you for investigating this issue. I apologize in advance if this 'bug' is an error on my part.

How to watch the entry directory for adding/removing/renaming a file?

What is the expected behavior?

The watching of the entry directory for changing of entry files and rebuild entries when files are added/deleted/renamed.

Problem

Compilation fails after renaming an entry file:

ERROR in <entry name>
Module not found: Error: Can't resolve <entry source file> in <entry dir>

Integrity and Crossorigin attributes do not generate for Script tags

Current behaviour

Setting integrity to true (or auto in production mode), does not cause the integrity or crossorigin attributes to get added to <script> tags.

Expected behavior

The integrity and crossorigin attributes should be automatically applied to Scripts and Styles.

Reproduction Example

Step 1: Checkout this repo and enable integrity
https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/29131635a37f9be1bd0e9187734d609d3178da99/webpack.config.js#L24

Step 2: Delete the following line to avoid reproducing the bug from #42
https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/29131635a37f9be1bd0e9187734d609d3178da99/src/views/home.ejs#L3

Step 3: npm run build to compile the application

Step 4: Observe that integrity and crossorigin attributes are missing from the output html file

Environment

  • OS: Windows
  • version of Node.js: v21.1.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 2.15.0

Custom preprocessor function doesn't watch files

Current behaviour

If I use a custom function for preprocessor then dev-server watch files and instead closes with success.

Expected behaviour

Dev server should watch template files

Reproduction Example

Run the liquid js example in the readme in watch mode

Environment

  • OS: macOS
  • version of Node.js: 16
  • version of Webpack: 5
  • version of the Plugin: 1.17.0

Additional context

RSPack support and compatibility

Feature request

What is motivation or use case for adding/changing the behavior?
Would be nice to add compatibility with rspack ecosystem as many other webpack plugins are already compatible with rspack. In my project, it's the only plugin that is incompatible with rspack and doesn't have any other equivalent

Useful links: https://www.rspack.dev/

Describe the solution you'd like
Resolve the error on the attached screenshot

rspack

Describe alternatives you've considered
None

Inlining imported css doesn't work in watch mode if a leaf component content is changed

Current behaviour

I have and index.html that imports index.jsx which has a Layout Component, and the Layout Component imports a stylesheet from a package in node_modules, and the Layout component renders a child component, when I run webpack --watch --progress --mode development for the first time I get all the js and css injected in the html however, if I change the child component file and save, the generated html won't have the inline css, furthermore, if I go to the Layout component and save it to trigger rebuild, I get the css in the generated html.

Expected behaviour

The css should be included in the generated html in watch mode every time

Reproduction Example

./index.html

<div id="root"></div>
<script src="./index.js"></script>

./index.js

import React from "react";
import * as ReactDOM from "react-dom/client";
import Layout from "./Layout";
Import SomeComponent from "./SomeComponent";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(<Layout><SomeComponent /></Layout>);

./Layout.jsx

import "some-package/dist/index.css";
import React from "react";

export default function Layout({children}){
  reutrn <div>{children}</div>
}

./SomeComponent.jsx

import React from "react";

export default function SomeComponent(){
  reutrn <div>Some content</div>
}

webpack.config.js

const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
const HtmlBundlerPlugin = require("html-bundler-webpack-plugin");

module.exports = (env) => {
  return {
    resolve: {
      extensions: [".tsx", ".jsx", ".ts", ".js"],
    },
    plugins: [
      new CopyPlugin({
        patterns: [
          {
            from: "./",
            to: "resources",
            filter: (file) => !file.endsWith(".jsx") && !file.endsWith(".html"),
          },
        ],
      }),
      new HtmlBundlerPlugin({
        filename: "[name].ftl",
        entry: "./",
        postprocess: (content) => {
          return content.concat("<head></head>");
        },
        js: {
          inline: true,
        },
        css: {
          inline: true,
        },
      }),
      {
        apply(compiler) {
          const pluginName = "inline-template-plugin";

          compiler.hooks.compilation.tap(pluginName, (compilation) => {
            const hooks = HtmlBundlerPlugin.getHooks(compilation);

            hooks.beforeEmit.tap(pluginName, (content) => {
              return (
                '<#import "template.ftl" as layout>' +
                "<@layout.registrationLayout displayInfo=social.displayInfo; section>" +
                content +
                "</@layout.registrationLayout>"
              );
            });
          });
        },
      },
    ],
    module: {
      rules: [
        {
          test: /.(js|jsx|ts|tsx)$/,
          use: {
            loader: "babel-loader",
            options: {
              plugins: [["remove-comments"]],
              presets: [["@babel/preset-env"], "@babel/preset-react"],
            },
          },
        },
        {
          test: /.(js|jsx|ts|tsx)$/,
          include: /node_modules/,
          use: {
            loader: "babel-loader",
          },
        },
        {
          test: /.(ts|tsx)$/,
          use: "ts-loader",
          exclude: /node_modules/,
        },
        {
          test: /\.(css|sass|scss)$/,
          use: ["css-loader", "sass-loader"],
        },
        {
          test: /\.(ico|png|jp?g|webp|svg)$/,
          type: "asset/resource",
          generator: {
            outputPath: () => {
              return "resources";
            },
            filename: ({ filename }) => {
              const base = path.basename(filename);
              return "/img/" + base;
            },
          },
        },
        {
          test: /[\\/]fonts|node_modules[\\/].+(woff(2)?|ttf|otf|eot)$/i,
          type: "asset/resource",

          generator: {
            outputPath: () => {
              return "resources";
            },
            filename: ({ filename }) => {
              const base = path.basename(filename);
              return "/fonts/" + base;
            },
          },
        },
      ],
    },
    output: {
      clean: true,
      publicPath: "public",
    },
    watchOptions: {
      ignored: ["node_modules", "dist"],
    },
    cache: {
      type: "memory",
      cacheUnaffected: false,
    },
    devtool: false,
  };
};

Environment

  • OS: Windows
  • version of Node.js: 21.2.0
  • version of Webpack: 5.90.1
  • version of the Plugin: 3.4.12

Additional context

I noticed that the beforeEmit hook isn't being called after saving SomeComponent while its being called for Layout component

fix attributes Type

Discussed in #34

Originally posted by vralle October 12, 2023
Hi! I've been using HTML Bundler Plugin for Webpack for a while. This is a great tool for creating website prototypes. As new tasks were implemented, the webpack config grew and I have enabled strict type checking.

loaderOptions.sources:

My config is working OK and I'm sure that attributes is an object. I believe this is a issue in the type export

attributes: string;

I use html-bundler-webpack-plugin v2.14.3

Ignored imports (where an alias is set to `false`) causes error

ERROR in [entry] [initial]
Cannot read properties of undefined (reading '_bundlerPluginMeta')
TypeError: Cannot read properties of undefined (reading '_bundlerPluginMeta')
    at walk (/home/david/code/metamask-extension2/node_modules/html-bundler-webpack-plugin/src/Plugin/Collection.js:529:67)

const { isImportedStyle } = depModule.resourceResolveData._bundlerPluginMeta || {}; // TODO: test

I think the fix might be to check if depModule.resourceResolveData is defined before continuing to walk the dependencies.

p.s., The // TODO: test existing on the same line as the failing code made me smile a little :-)

Minimal Reproduction of `webpack.cache.type="filesystem"` error

I know the README says the filesystem cache is experimental, but it seems like it works sometimes (it only seems to fail in watch mode in one of my very large projects). I've got a minimal reproduction of the issue below.

What would it take to get this to work?

Reproduction

Create the following files (files are below the output examples), and then run yarn webpack twice.

First run succeeds, but with a warning:

~/code/test/cachetest $ yarn webpack
yarn run v1.22.19
$ /home/david/code/test/cachetest/node_modules/.bin/webpack
<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'html-bundler-webpack-plugin|PersistentCache': Unexpected function (pathData, assetInfo) => {
<w>       // the template filename stays the same after changes in watch mode because have not a hash substitution
<w>       if (assetEntryOptions.isTemplate && assetEntryOptions.filename != null) return assetEntryOptions.filename;
<w> 
<w>       let filename = filenameTemplate;
<w> 
<w>       if (isFunction(filenameTemplate)) {
<w>         // clone the pathData object to modify the chunk object w/o side effects in the main compilation
<w>         const pathDataCloned = { ...pathData };
<w>         pathDataCloned.chunk = { ...pathDataCloned.chunk };
<w>         if (originalName) {
<w>           pathDataCloned.chunk.name = originalName;
<w>           pathDataCloned.chunk.runtime = originalName;
<w>         }
<w> 
<w>         // the `filename` property of the `PathData` type should be a source file, but in entry this property not exists
<w>         if (pathDataCloned.filename == null) {
<w>           pathDataCloned.filename = assetEntryOptions.sourceFile;
<w>         }
<w> 
<w>         filename = filenameTemplate(pathDataCloned, assetInfo);
<w>       }
<w> 
<w>       if (assetEntryOptions.publicPath) {
<w>         filename = path.posix.join(assetEntryOptions.publicPath, filename);
<w>       }
<w> 
<w>       return filename;
<w>     }
<w> while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> /home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/createPersistentCache.js.PersistentCache -> Map { 1 items } -> Object { entry, assets } -> Object { id, name, originalName, filenameTemplate, filename, assetFile, resource, importFile, sourceFile, sourcePath, outputPath, publicPath, library, verbose, isTemplate, isStyle, filenameFn } -> (pathData, assetInfo) => {
<w>       // the template filename stays the same after changes in watch mode because have not a hash substitution
<w>       if (assetEntryOptions.isTemplate && assetEntryOptions.filename != null) return assetEntryOptions.filename;
<w> 
<w>       let filename = filenameTemplate;
<w> 
<w>       if (isFunction(filenameTemplate)) {
<w>         // clone the pathData object to modify the chunk object w/o side effects in the main compilation
<w>         const pathDataCloned = { ...pathData };
<w>         pathDataCloned.chunk = { ...pathDataCloned.chunk };
<w>         if (originalName) {
<w>           pathDataCloned.chunk.name = originalName;
<w>           pathDataCloned.chunk.runtime = originalName;
<w>         }
<w> 
<w>         // the `filename` property of the `PathData` type should be a source file, but in entry this property not exists
<w>         if (pathDataCloned.filename == null) {
<w>           pathDataCloned.filename = assetEntryOptions.sourceFile;
<w>         }
<w> 
<w>         filename = filenameTemplate(pathDataCloned, assetInfo);
<w>       }
<w> 
<w>       if (assetEntryOptions.publicPath) {
<w>         filename = path.posix.join(assetEntryOptions.publicPath, filename);
<w>       }
<w> 
<w>       return filename;
<w>     }
asset main.js 1.2 KiB [emitted] (name: main)
asset index.html 124 bytes [compared for emit] (name: __bundler-plugin-entry__index)
./src/index.html 206 bytes [built] [code generated]
./src/main.js 27 bytes [built] [code generated]
 HTML Bundler Plugin  ▶▶▶ (webpack 5.89.0) compiled successfully in 50 ms
Done in 1.00s.

Second run fails:

~/code/test/cachetest $ yarn webpack
yarn run v1.22.19
$ /home/david/code/test/cachetest/node_modules/.bin/webpack
<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'html-bundler-webpack-plugin|PersistentCache': Unexpected function (pathData, assetInfo) => {
<w>       // the template filename stays the same after changes in watch mode because have not a hash substitution
<w>       if (assetEntryOptions.isTemplate && assetEntryOptions.filename != null) return assetEntryOptions.filename;
<w> 
<w>       let filename = filenameTemplate;
<w> 
<w>       if (isFunction(filenameTemplate)) {
<w>         // clone the pathData object to modify the chunk object w/o side effects in the main compilation
<w>         const pathDataCloned = { ...pathData };
<w>         pathDataCloned.chunk = { ...pathDataCloned.chunk };
<w>         if (originalName) {
<w>           pathDataCloned.chunk.name = originalName;
<w>           pathDataCloned.chunk.runtime = originalName;
<w>         }
<w> 
<w>         // the `filename` property of the `PathData` type should be a source file, but in entry this property not exists
<w>         if (pathDataCloned.filename == null) {
<w>           pathDataCloned.filename = assetEntryOptions.sourceFile;
<w>         }
<w> 
<w>         filename = filenameTemplate(pathDataCloned, assetInfo);
<w>       }
<w> 
<w>       if (assetEntryOptions.publicPath) {
<w>         filename = path.posix.join(assetEntryOptions.publicPath, filename);
<w>       }
<w> 
<w>       return filename;
<w>     }
<w> while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> /home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/createPersistentCache.js.PersistentCache -> Map { 1 items } -> Object { entry, assets } -> Object { id, name, originalName, filenameTemplate, filename, assetFile, resource, importFile, sourceFile, sourcePath, outputPath, publicPath, library, verbose, isTemplate, isStyle, filenameFn } -> (pathData, assetInfo) => {
<w>       // the template filename stays the same after changes in watch mode because have not a hash substitution
<w>       if (assetEntryOptions.isTemplate && assetEntryOptions.filename != null) return assetEntryOptions.filename;
<w> 
<w>       let filename = filenameTemplate;
<w> 
<w>       if (isFunction(filenameTemplate)) {
<w>         // clone the pathData object to modify the chunk object w/o side effects in the main compilation
<w>         const pathDataCloned = { ...pathData };
<w>         pathDataCloned.chunk = { ...pathDataCloned.chunk };
<w>         if (originalName) {
<w>           pathDataCloned.chunk.name = originalName;
<w>           pathDataCloned.chunk.runtime = originalName;
<w>         }
<w> 
<w>         // the `filename` property of the `PathData` type should be a source file, but in entry this property not exists
<w>         if (pathDataCloned.filename == null) {
<w>           pathDataCloned.filename = assetEntryOptions.sourceFile;
<w>         }
<w> 
<w>         filename = filenameTemplate(pathDataCloned, assetInfo);
<w>       }
<w> 
<w>       if (assetEntryOptions.publicPath) {
<w>         filename = path.posix.join(assetEntryOptions.publicPath, filename);
<w>       }
<w> 
<w>       return filename;
<w>     }
Entrypoint __bundler-plugin-entry__index =
cached modules 233 bytes [cached] 2 modules

ERROR in [entry] [initial]
 html-bundler-webpack-plugin  Failed to execute the function.
Source file: '/home/david/code/test/cachetest/src/index.html'
/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Messages/Exception.js:109
  throw new PluginException(message);
  ^

PluginException: 
 html-bundler-webpack-plugin  Can't resolve src/main.js in the file src/index.html

    at resolveException (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Messages/Exception.js:109:9)
    at Resolver.require (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Resolver.js:262:7)
    at /home/david/code/test/cachetest/src/index.html:1:116
    at Script.runInContext (node:vm:135:12)
    at VMScript.compile (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/VMScript.js:37:35)
    at Plugin.renderModule (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:1226:27)
    at Plugin.renderManifest (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:860:26)
    at Hook.eval [as call] (eval at create (/home/david/code/test/cachetest/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:16)
    at Hook.CALL_DELEGATE [as _call] (/home/david/code/test/cachetest/node_modules/tapable/lib/Hook.js:14:14)
    at Compilation.getRenderManifest (/home/david/code/test/cachetest/node_modules/webpack/lib/Compilation.js:4525:36)
PluginException: 
 html-bundler-webpack-plugin  Failed to execute the function.
Source file: '/home/david/code/test/cachetest/src/index.html'
/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Messages/Exception.js:109
  throw new PluginException(message);
  ^

PluginException: 
 html-bundler-webpack-plugin  Can't resolve src/main.js in the file src/index.html

    at resolveException (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Messages/Exception.js:109:9)
    at Resolver.require (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Resolver.js:262:7)
    at /home/david/code/test/cachetest/src/index.html:1:116
    at Script.runInContext (node:vm:135:12)
    at VMScript.compile (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/VMScript.js:37:35)
    at Plugin.renderModule (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:1226:27)
    at Plugin.renderManifest (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:860:26)
    at Hook.eval [as call] (eval at create (/home/david/code/test/cachetest/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:16)
    at Hook.CALL_DELEGATE [as _call] (/home/david/code/test/cachetest/node_modules/tapable/lib/Hook.js:14:14)
    at Compilation.getRenderManifest (/home/david/code/test/cachetest/node_modules/webpack/lib/Compilation.js:4525:36)
    at executeFunctionException (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/Messages/Exception.js:120:9)
    at VMScript.compile (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/VMScript.js:41:7)
    at Plugin.renderModule (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:1226:27)
    at Plugin.renderManifest (/home/david/code/test/cachetest/node_modules/html-bundler-webpack-plugin/src/Plugin/AssetCompiler.js:860:26)
    at Hook.eval [as call] (eval at create (/home/david/code/test/cachetest/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:16)
    at Hook.CALL_DELEGATE [as _call] (/home/david/code/test/cachetest/node_modules/tapable/lib/Hook.js:14:14)
    at Compilation.getRenderManifest (/home/david/code/test/cachetest/node_modules/webpack/lib/Compilation.js:4525:36)
    at /home/david/code/test/cachetest/node_modules/webpack/lib/Compilation.js:4545:22
    at symbolIterator (/home/david/code/test/cachetest/node_modules/neo-async/async.js:3482:9)
    at timesSync (/home/david/code/test/cachetest/node_modules/neo-async/async.js:2297:7)

 HTML Bundler Plugin  ▶▶▶ (webpack 5.89.0) compiled with 1 error in 48 ms
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Here are the files:

// webpack.config.js
const { join } = require("node:path");
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
    mode: "development",
    cache: {
        type: "filesystem"
    },
    plugins: [
        new HtmlBundlerPlugin({
            entry: {
                index: join(__dirname, "src", "index.html"),
            }
        })
    ]
};
<!-- src/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>My Page</title>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>
// src/main.js
console.log("hello world");
{
  "name": "cache-test",
  "version": "0.0.1",
  "scripts": {
    "build": "webpack"
  },
  "license": "MIT",
  "devDependencies": {
   "html-bundler-webpack-plugin": "^3.4.11",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4"
  }
}

Can't resolve image in seperate folder

Hello,

   html-bundler-webpack-plugin  Can't resolve src\static\images\Logo.png in the file src\views\index.html

It's called in this eta template that:

<header>
	<img src="../static/images/Logo.png" />
</header>

That is then referenced in this view:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Test</title>
		<%~ include('./src/components/head.eta') %>
	</head>
	<body>
		<%~ include('./src/components/header.eta') %>
	</body>
</html>

And, the file does exist.

Here is the webpack config

'use strict';
const path = require( 'path' );
const HtmlBundlerPlugin = require( 'html-bundler-webpack-plugin' );

module.exports = ( env, argv ) => [
	{
		devtool: ( argv.mode === 'development' ) ? 'inline-source-map' : false,
		output: {
			path: path.resolve( __dirname, 'build' ),
			clean: true
		},
		plugins: [
			new HtmlBundlerPlugin({
				entry: './src/views',
				hotUpdate: 'auto', // doesn't seem to work
				minify: 'auto',
				css: { inline: true }
			})
		],
		module: {
			rules: [
				{
					test: /\.(css|sass|scss)$/,
					use: [ 'css-loader', 'sass-loader' ]
				}
			]
		},
		devServer: {
			hot: false,
			port: 8000,
			allowedHosts: 'auto',
			static: path.resolve( __dirname, 'build' ),
			watchFiles: 'src/**/*',
			client: { progress: true },
			devMiddleware: { writeToDisk: true }
		}
	}
];

Thank you!

Mix use entry in plugin and in webpack

Mix use entry in plugin and in webpack

I want to take advantage of the recursive entries in the plugin config which is very convenient, but I also have non-html entry points that I want to specify in webpack-level entry field. I'm wondering what is the best practice to do so.
It would be very nice if anyone can guide me over that.

Callback onBeforeRender

Feature request

What is motivation or use case for adding/changing the behavior?
Would be great to have callbacks like for example onBeforeRender where I can manipulate data from json and render them after some changes etc.

Describe the solution you'd like

HandlebarsPlugin has severa callbacks - onBeforeSetup, onBeforeRender, onBeforeSave, onDone. I bet in your plugin are they too but aren't accessible.

  • onBeforeSetup - this was the point to register custom helpers - this plugin does it inside preprocessorOptions
  • onBeforeRender - this is what I am interested in - good for replacements inside json data file.
{
    "replacements": {
        "[sitename]": "ACME"
    },
    "meta": {
        "name": "ACME",
        "description": "",
    },
    "page": {
        "home": {
            "title": "Welcome to [sitename] website",
            "title_alt": "Welcome to {{meta.name}} website"
        }
    }
}

Imagine you have website name which can be used all across the website and also in text like Welcome to [sitename] website which in this example should return as Welcome to ACME website. Or instead would be great to use it like Welcome to {{meta.name}} website - this though is obviously not working.

  • onBeforeSave - I used this to manipulate dir/file structure - this plugin handles it in preprocessorOptions
  • onDone - I havent never used

Describe alternatives you've considered

I haven't found any alternative yet. Only write the full string manually.

Specifying custom path in output .html file for .css file url inside <link> tag

I have directory structure as shown below. My webpack.prod.js file uses html-bundler-webpack-plugin as shown below.

The output as expected gives all .css and .js files inside build/static directory and all .html files inside build/html directory.
The output html files have <link> tags that point to css like so href=/static/<filename>.css

Can I specify a custom path so that href looks like href=/filename.css while still preserving the output directory structure shown below and having all .css files inside build/static.

Why I want to do this? When I serve the output html files using node js, the path inside <link> is interpreted as relative to the url in the web browser. This way, I can have express.static serve the static folder with all static .css files inside it while controlling access to the html files rather than having all html files and static files inside one directory.

new HtmlBundlerWebpackPlugin({
    entry: './src/templates/',
    outputPath: path.join(__dirname, 'build/html/'),
    js: {
        filename: 'static/[name]-[contenthash].js',
    },
    css: {
        filename: 'static/[name]-[contenthash].css',
    },
})
| build
  | html
  | static
| package.json
| webpack.prod.js
| src
  | css
    | main.css
    | another.css
    | ...
  | images
    | image1.png
      ...
  | scripts
    | script1.js
    | script2.js
  | templates
    | file1.html
    | file2.html
      ...
| ...

Process asset references in meta tags

Current behaviour

Image url references in a meta tag aren't processed to the output directory.

Expected behaviour

meta tags are processed the same as img, link, etc.

Reproduction Example

 <meta property="og:image" content="../assets/og.png" />

Environment

  • OS: macOS
  • version of Node.js: 19.6.1
  • version of Webpack: 5.77.0
  • version of the Plugin: 1.13.0

Additional context

If I put the same url in a img tag: <img src="../assets/og.png" /> it gets processed so im guessing it's trying to parse specific tags? Haven't dug through the source code yet.

Using EJS if that matters.

[FEATURE REQUEST] Support the CSS extraction from styles used in *.vue files

Current behaviour

Build time is incredibly increased if there is an import of vue sfc components in the code, from 4 seconds to 80. If there is a link to the style file in this component, then the project is not built and crashes with an error.

Reproduction Example

I used vue-loader for processing sfc components, swc-loader for js/ts files and sass-loader for style files. And of course your plugin, if you remove it from the configuration and transfer only the ts file to webpack, everything works as expected

Environment

  • OS: Linux
  • version of Node.js: 18.18.2
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.0.3

Additional context

After digging a little in the code, putting console.log wherever possible, I got to this place Collection->findDependenciesOfModule->walk, it looks like this function gets into some kind of infinite recursion, I may be wrong, but I hope it helps you somehow

Style Sheets break Integrity "AssetCompiler.js"

Current behaviour

Setting integrity to true (or auto in production mode), causes the following error to occur if you reference .css or .scss files, whether it is from a .ejs or a .js file:

[webpack-cli] Error: TypeError: Cannot read properties of undefined (reading 'source')
    at Plugin.done (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\html-bundler-webpack-plugin\src\Plugin\AssetCompiler.js:1224:13)
    at Hook.eval [as callAsync] (eval at create (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:9:1)
    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\tapable\lib\Hook.js:18:14)
    at C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\webpack\lib\Compiler.js:498:23
    at Compiler.emitRecords (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\webpack\lib\Compiler.js:919:5)
    at C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\webpack\lib\Compiler.js:490:11
    at C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\webpack\lib\Compiler.js:885:14
    at Hook.eval [as callAsync] (eval at create (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:24:1)    
    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\tapable\lib\Hook.js:18:14)
    at C:\Users\steff\Desktop\html-bundler-webpack-plugin-bug-examples\node_modules\webpack\lib\Compiler.js:882:27

Expected behaviour

Integrity should work for css/scss files.

Reproduction Example

https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/29131635a37f9be1bd0e9187734d609d3178da99/webpack.config.js#L24

If you enable integrity and execute npm run build the error is produced.

If you delete this line, the error no longer will be produced and output will occur:
https://github.com/SteffenBlake/html-bundler-webpack-plugin-bug-examples/blob/29131635a37f9be1bd0e9187734d609d3178da99/src/views/home.ejs#L3

Environment

  • OS: Windows
  • version of Node.js: v21.1.0
  • version of Webpack: 5.89.0
  • version of the Plugin: 2.15.0

How can I keep the original `.php` file extension?

Discussed in #3

Originally posted by Raxel21 March 21, 2023
Hi, I hope everything is going well for you, today I discovered your plugin and I find it very useful.

Well, I am having a problem working with .php, with .html it works perfectly. I tried to adapt the code but I did not succeed.

I tried 2 ways

It works, but it changes the extension to .html, so it breaks the PHP code.

new HtmlBundlerPlugin({
  test: /\.php$/i,
  verbose: true,
  entry: {
    index: path.join(__dirname, 'src', 'public', 'index.php'),
  },
  js: {
    filename: '[name].[contenthash:8].js',
  },
  css: {
    filename: '[name].[contenthash:8].css',
  },
})

So I tried this other way

It also works, but it keeps changing the extension to .html, but this time I get an error:
html-bundler-webpack-plugin: Failed to execute the template function
I thought that just returning the template in the preprocessor line would work but I get the above error.

new HtmlBundlerPlugin({
  test: /\.php$/i,
  verbose: true,
  entry: {
    home: {
      import: path.join(__dirname, 'src', 'public', 'index.php'),
      filename: 'index.php',
    },
  },
  js: {
    filename: '[name].[contenthash:8].js',
  },
  css: {
    filename: '[name].[contenthash:8].css',
  },
  loaderOptions: {
    preprocessor: (template, { data }) => template,
  },
})

Any suggestions please. Or maybe I forgot something from the documentation?
Thank you in advance

Issue with integrating html-bundler-webpack-plugin with React in a multi-page setup

Hello @webdiscus , I hope you're doing well. I have an issue regarding the usage of the html-bundler-webpack-plugin with React in a multi-page setup. I'm currently working on a project and I want to integrate this plugin with React, but I'm encountering some difficulties. I'm unsure about the proper setup and configuration to ensure seamless compatibility between the plugin and React. Could you please provide some guidance or instructions on how to effectively use the html-bundler-webpack-plugin with React in a multi-page setup? It would be great if you could share any insights, best practices, or examples to help me resolve this issue. Thank you for your time and assistance. I look forward to your response!

Bundle NextJS static export into single index.html file

Hi everyone,

My goal is to bundle a NextJS application into a single index.html file. I was very happy to stumble across this plugin, as it seems very promising for this task!

What I've done:

  1. Create NextJS App
  2. Run Static Export -> Generates out/ directory with the following structure:
- out
  - \_next
    - lvHzHE4P5UBVueHLgV9u6J
  - static
    - chunks
      - app
        - _not-found-be6954023b538636.js
        - layout-8c4f8055601f861.js
        - page-390a08b8bc838c8.js
      - pages
        - _app-2727a7117f49dcf1.js
        - _error-91a5938854a6f402.js
      - 864-5b2a0e4b041824d8.js
      - 964-ed976b209e7a86b0.js
      - fd91056d-bdc2f178762b876.js
      - framework-8883d1e9be70c3da.js
      - main-app-a5c502c4547e2c37.js
      - main-eaac6a2efdb8f260.js
      - polyfills-c67a75d1bf699dc8.js
      - webpack-bba7619efc7529ac.js
      - css
        - 8aba845b4757a42c.css
    - lvHzHE4P5UBVueHLgV9u6J
        - _buildManifest.js
        - _ssgManifest.js
    - media
      - ...
    - 404.html
    - favicon.ico
    - index.html
    - index.txt
    - next.svg
    - vercel.svg
  1. Run webpack with the following config:
const HtmlBundlerPlugin = require("html-bundler-webpack-plugin")

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      entry: "./out/",

      js: {
        filename: "assets/js/[name].[contenthash:8].js",
        inline: true, // inlines JS into HTML
      },
      css: {
        filename: "assets/css/[name].[contenthash:8].css",
        inline: true, // inlines CSS into HTML
      }
    })
  ],

  mode: "production",
}
  1. The output is dist/ directory with 404.html and index.html, but unfortunately the scripts are not inlined.

The repository with everything I mentioned can be found here: https://github.com/kannwism/next-js-single-html/

I'm new to webpack so I don't know if this is a user error / misconfiguration on my side, or if this is a bug or unsupported feature of the plugin.

In any case, there might be other people interested in achieving this, e.g. on reddit.

I know that NextJS is not meant to do this, but our team has already put a lot of effort into the NextJS app.

Any help is highly appreciated!

HMR doesn't work for pages if all scripts are commented out

Current behaviour

If a page doesn't have any script tags then HMR doesn't load always even though the build output shows that hot-update.js file has been added to assets.

Expected behaviour

HMR should work by adding the empty js file. But it is not added to all pages.

Reproduction Example

This repo https://github.com/rohit-gohri/html-bundler-webpack-plugin-hmr-issue created from the boilerplate and modified to remove scripts from the template has the /demo.html page no longer hot-reloading on changes.

Environment

  • OS: macOS
  • version of Node.js: 16
  • version of Webpack: 5
  • version of the Plugin: 1.17.1

Additional context

If I remove this !hasScript condition in my node _modules then it works:

if (this.hot && !Collection.hasScript(issuer)) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.