Skip to content

Commit

Permalink
fix: infinity walk by circular dependency, #59
Browse files Browse the repository at this point in the history
  • Loading branch information
webdiscus committed Dec 28, 2023
1 parent 3394c31 commit 606eeab
Show file tree
Hide file tree
Showing 323 changed files with 3,064 additions and 1,860 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change log

## 3.4.4 (2023-12-28)

- fix: extract css from complex libs like MUI leads to an infinity walk by circular dependency, #59
- test: refactor tests

## 3.4.3 (2023-12-18)

- fix: favicon plugin causes a crash without an error explaining if no link tag is included
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ See [boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate)
- Automatically processing **multiple HTML** templates [View in browser](https://stackblitz.com/edit/webpack-webpack-js-org-diop8g?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/simple-site/)
- **Bootstrap** with Webpack [View in browser](https://stackblitz.com/edit/webpack-webpack-js-org-kjnlvk?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/bootstrap)
- **Tailwind CSS** with Webpack [View in browser](https://stackblitz.com/edit/webpack-webpack-js-org-auem8r?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/tailwindcss/)
- **Twig** with Webpack [View in browser](https://stackblitz.com/edit/twig-webpack?file=webpack.config.js)
- **Handlebars** with Webpack [View in browser](https://stackblitz.com/edit/webpack-webpack-js-org-mxbx4t?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/handlebars/)
- Extend **Handlebars layout** with blocks [View in browser](https://stackblitz.com/edit/webpack-webpack-js-org-bjtjvc?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/handlebars-layout/)
- Auto generate **integrity hash** for `link` and `script` tags [View in browser](https://stackblitz.com/edit/webpack-integrity-hvnfmg?file=webpack.config.js) | [source](https://github.com/webdiscus/html-bundler-webpack-plugin/tree/master/examples/integrity/)
Expand Down Expand Up @@ -3516,6 +3517,13 @@ You can use a relative path:
> {% extends myTemplate %}
> ```

> **Warning**
>
> The Twig template containing `tabs` will not be compiled into HTML.\
> Use the `spaces` as an indent in templates.
> The `tabs` are not supported by `TwigJS`.


---

#### [↑ back to contents](#contents)
Expand Down
3,968 changes: 2,496 additions & 1,472 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
"version": "3.4.3",
"version": "3.4.4",
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
"keywords": [
"html",
Expand Down Expand Up @@ -120,42 +120,42 @@
"html-minifier-terser": "^7.2.0"
},
"devDependencies": {
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@emotion/react": "^11.11.1",
"@babel/core": "^7.23.6",
"@babel/preset-env": "^7.23.6",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.14.19",
"@mui/material": "^5.15.2",
"@test/html-bundler-webpack-plugin": "file:./",
"@test-fixtures/js": "0.0.2",
"@test-fixtures/dius": "file:./test/fixtures/node_modules/dius/",
"@test-fixtures/lorem": "file:./test/fixtures/node_modules/lorem/",
"@test-fixtures/scss": "0.0.7",
"@test/import-css": "file:./test/fixtures/node_modules/import-css/",
"@types/jest": "^29.5.10",
"@types/jest": "^29.5.11",
"copy-webpack-plugin": "9.1.0",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"ejs": "^3.1.9",
"favicons": "^7.1.4",
"favicons": "7.1.4",
"handlebars": "^4.7.8",
"handlebars-layouts": "^3.1.4",
"jest": "^29.7.0",
"liquidjs": "^10.9.4",
"liquidjs": "^10.10.0",
"mustache": "^4.2.0",
"nunjucks": "^3.2.4",
"prettier": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"prettier": "^3.1.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"responsive-loader": "^3.1.2",
"rtlcss": "^4.1.1",
"sass": "1.67.0",
"sass-loader": "13.3.2",
"sass-loader": "13.3.3",
"sharp": "^0.32.6",
"ts-loader": "9.5.1",
"typescript": "5.3.2",
"typescript": "5.3.3",
"twig": "^1.17.1",
"vue": "^3.3.9",
"vue-loader": "^17.3.1",
"vue": "3.3.13",
"vue-loader": "^17.4.0",
"webpack": "^5.89.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "^4.15.1"
Expand Down
13 changes: 8 additions & 5 deletions src/Plugin/AssetCompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,6 @@ class AssetCompiler {
}

AssetEntry.connectEntryAndModule(module, resolveData);
// for debug only, don't use this id as real entryId, because the module.resourceResolveData is cached in serve mode
meta._lastEntryId = resolveData.entryId;

// skip the module loaded via importModule
if (meta.isLoaderImport || meta.isParentLoaderImport) return;
Expand Down Expand Up @@ -1014,7 +1012,12 @@ class AssetCompiler {
const modules = Collection.findImportedModules(entry.id, issuer, chunk);

// 2. squash styles from all nested files into one file
for (const module of modules) {
const uniqueModuleIds = new Set();
for (const { module } of modules) {
if (uniqueModuleIds.has(module.debugId)) {
continue;
}

const isUrl = module.resourceResolveData?.query.includes('url');
const importData = {
resource: module.resource,
Expand Down Expand Up @@ -1044,7 +1047,7 @@ class AssetCompiler {
}

if (isUrl) {
// get url of css output filename in js for lazy load
// get url of css output filename in js for the lazy load
Collection.setData(
entry,
{ resource: issuer },
Expand All @@ -1062,9 +1065,9 @@ class AssetCompiler {

cssHash += module.buildInfo.hash;
sources.push(...module._cssSource);

imports.push(importData);
resources.push(module.resource);
uniqueModuleIds.add(module.debugId);
}

if (sources.length === 0) continue;
Expand Down
46 changes: 14 additions & 32 deletions src/Plugin/Collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ class Collection {
}

/**
* Find styles from all nested JS files imported in the root JS file and sort them.
* Find styles from all nested JS files.
*
* @param {number} entryId The entry id of the template where is the root issuer.
* Note: the same issuer can be used in many entries.
Expand All @@ -462,38 +462,12 @@ class Collection {
static findImportedModules(entryId, rootIssuer, chunk) {
const issuerModule = this.getGraphModule(rootIssuer);
const modules = this.findModuleDependencies(issuerModule);
const uniqueModules = [];

// reserved for debug;
// the modules are already sorted
//modules.sort((a, b) => (a.order < b.order ? -1 : 1));

// TODO: research how to unique modules used in the current issuer and in deep nested issuers;
// Currently the prio is for an first exemplar in nested issuers.
// See the test js-import-css-order-dependencies.
// For example:
// 0: a.css
// 1.0: common.css // currently, is kept this file (imported in the nested js component)
// 3: b.css
// 2: common.css // perhaps, should be kept this?
for (const { module } of modules) {
if (!uniqueModules.includes(module)) {
uniqueModules.push(module);
}
}

// reserved for debug
// console.log(
// '\n### modules:\n',
// modules.map(({ order, module }) => {
// return {
// order,
// resource: path.basename(module.resource),
// };
// })
// );

return uniqueModules;
return modules;
}

/**
Expand All @@ -502,13 +476,20 @@ class Collection {
*/
static findModuleDependencies(module) {
const { moduleGraph } = this.compilation;
const circularDependencyIds = new Set();
const orderStack = [];
let order = '';
let orderStack = [];

const walk = (module) => {
const { dependencies } = module;
const result = [];

// avoid an infinity walk by circular dependency
if (circularDependencyIds.has(module.debugId)) {
return result;
}
circularDependencyIds.add(module.debugId);

for (const dependency of dependencies) {
// TODO: detect whether the userRequest is a file, not a runtime, e.g. of vue
if (
Expand All @@ -531,16 +512,17 @@ class Collection {
depModule = depModule.rootModule;
}

const parentIndex = moduleGraph.getParentBlockIndex(dependency);
const index = moduleGraph.getParentBlockIndex(dependency);

if (depModule.dependencies.length > 0) {
// save current order before recursive walking
orderStack.push(order);
order += (order ? '.' : '') + parentIndex;
order += (order ? '.' : '') + index;
result.push(...walk(depModule));
} else if (depModule.resourceResolveData?._bundlerPluginMeta.isImportedStyle === true) {
result.push({
order: order + (order ? '.' : '') + parentIndex,
resource: depModule.resource,
order: order + (order ? '.' : '') + index,
module: depModule,
});
}
Expand Down
2 changes: 1 addition & 1 deletion test/cases/decode-chars/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/entry-css-single/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

const languages = {
'de-DE': require('./src/locales/de-DE.json'),
Expand Down
2 changes: 1 addition & 1 deletion test/cases/entry-data-i18n-multipage/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

const languages = {
'de-DE': require('./src/locales/de-DE.json'),
Expand Down
2 changes: 1 addition & 1 deletion test/cases/entry-js-css/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/entry-name-html-js-css/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hbs-access-root-variable/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hello-world/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hook-afterEmit/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const fs = require('fs');
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

/**
* @param {CompileEntries} entries
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hook-beforeEmit/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hook-beforePreprocessor/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

// custom sync preprocessor function
const render = (template, search, replacement) => template.replace(search, replacement);
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hook-postprocess/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/hook-preprocessor/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

// custom sync preprocessor function
const render = (template, search, replacement) => template.replace(search, replacement);
Expand Down
2 changes: 1 addition & 1 deletion test/cases/ignore-webpack-entry/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/import-css-from-module-wo-ext/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/import-raw-html/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/import-url-in-css/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/import-url-in-scss/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/inline-asset-bypass-data-url/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'development',
Expand Down
2 changes: 1 addition & 1 deletion test/cases/inline-asset-decide-size/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const HtmlBundlerPlugin = require('../../../');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'development',
Expand Down
Loading

0 comments on commit 606eeab

Please sign in to comment.