Skip to content

Commit

Permalink
optimize: lazy load the plugin config file
Browse files Browse the repository at this point in the history
  • Loading branch information
webdiscus committed Mar 3, 2024
1 parent bae5691 commit b156817
Show file tree
Hide file tree
Showing 62 changed files with 410 additions and 45 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Change log

## 3.5.4 (2024-03-03)

- optimize: lazy load the plugin config file
- refactor: change the label in output: `html-bundler-webpack-plugin` => `HTML bundler plugin`
- refactor: optimize code for other plugins extending from this plugin.\
For example: the [pug-plugin](https://github.com/webdiscus/pug-plugin) since the version `5.0.0` is extended from the `html-bundler-webpack-plugin` with Pug specifically settings.
- docs: add description how to use the `entry` plugin option as the array of the `EntryDescription`, e.g.:
```js
{
entry: [
{
filename: 'index.html', // output filename in dist/
import: 'src/views/index.html', // template file
data: { title: 'Homepage' }, // page specifically variables
},
{
filename: 'news/sport.html',
import: 'src/views/news/sport/index.html',
data: { title: 'Sport' },
},
],
}
```

## 3.5.3 (2024-02-28)

- fix: correct parsing the data passed via query in JSON notation, e.g.: `index.ejs?{"title":"Homepage","lang":"en"}`
Expand Down
65 changes: 62 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@ See [boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate)
1. [Plugin options](#plugin-options)
- [test](#option-test) (RegEx to handle matching templates)
- [entry](#option-entry) (entry as a list of template files)
- [entry as an array](#option-entry-array) (array notation)
- [entry as an object](#option-entry-object) (object notation)
- [entry as a path](#option-entry-path) (find templates in a directory recursively)
- [entry data](#option-entry-data) (pass data in the single template as an object or a file)
- [entry dynamic](#option-entry-path) (entry as a path to template files)
- [outputPath](#option-outputpath) (output path of HTML file)
- [filename](#option-filename) (output filename of HTML file)
Expand Down Expand Up @@ -467,7 +471,7 @@ See [boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate)
- [pug](#loader-option-preprocessor-options-pug)
- [twig](#loader-option-preprocessor-options-twig)
- [custom](#loader-option-preprocessor-custom) (using any template engine)
- [data](#loader-option-data) (pass data into templates)
- [data](#loader-option-data) (pass global data into all templates as an object or a file)
1. [Using template engines](#template-engine)
- [Eta](#using-template-eta)
- [EJS](#using-template-ejs)
Expand Down Expand Up @@ -1219,7 +1223,7 @@ This plugin can completely replace the functionality of `mini-css-extract-plugin
### `entry`
Type: `EntryObject | string`.
Type: `EntryObject | Array<EntryDescription> | string`.
The `EntryObject` is identical to [Webpack entry](https://webpack.js.org/configuration/entry-context/#entry)
plus additional `data` property to pass custom variables into the HTML template.
Expand Down Expand Up @@ -1384,7 +1388,62 @@ To pass global variables in all templates use the [data](#loader-option-data) lo
> **Note**
>
> You can define templates both in Webpack `entry` and in the `entry` option of the plugin. The syntax is identical.
> But the `data` property can only be defined in the `entry` option of the plugin.
> But the `data` property can only be used in the `entry` option of the plugin.
<a id="option-entry-array" name="option-entry-array"></a>
#### Entry as an array
If the `entry` is the array of the `EntryDescription` then the `filename` property is required.
```js
{
entry: [
{
filename: 'index.html', // => output filename dist/index.html
import: 'src/views/index.html', // template file
data: { title: 'Homepage' }, // page specifically variables
},
{
filename: 'about.html',
import: 'src/views/about.html',
data: { title: 'About' },
},
{
filename: 'news/sport.html',
import: 'src/views/news/sport.html',
data: { title: 'Sport' },
},
],
}
```
<a id="option-entry-object" name="option-entry-object"></a>
#### Entry as an object
The absolute equivalent to the example above using an object is:
```js
{
entry: {
index: { // => output filename dist/index.html
import: 'src/views/index.html', // template file
data: { title: 'Homepage' }, // page specifically variables
},
about: {
import: 'src/views/about.html',
data: { title: 'About' },
},
'news/sport': {
import: 'src/views/news/sport.html',
data: { title: 'Sport' },
},
},
}
```
The difference between **object** and **array** notation:
- Using the **object** notation the output **filename** is the key of the entry item without the `.html` file extension.
- Using the **array** notation the output **filename** is the `filename` property of the array item contained the file with `.html` file extension.
<a id="option-entry-path" name="option-entry-path"></a>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
"version": "3.5.3",
"version": "3.5.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
40 changes: 40 additions & 0 deletions src/Common/Config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const path = require('path');

class Config {
static #loaded = false;
static #configFile = '';
static #config = {
// plugin name, must be same as node module name
pluginName: 'webpack-plugin',
// label in terminal output from plugin
pluginLabel: 'plugin',
// label in terminal output from loader
loaderLabel: 'loader',
};

static init(file) {
this.#configFile = path.isAbsolute(file)
? require.resolve(file)
: // path relative to `./src/Common`
require.resolve(path.join('../', file));
}

static get() {
// lazy load config data
if (!this.#loaded) {
this.#load();
}

return this.#config;
}

static #load() {
if (this.#configFile) {
let config = require(this.#configFile);
this.#config = Object.assign(this.#config, config);
this.#loaded = true;
}
}
}

module.exports = Config;
4 changes: 3 additions & 1 deletion src/Common/FileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

const path = require('path');
const { isWin, pathToPosix } = require('./Helpers');
const { pluginName } = require('../config');
const Config = require('../Common/Config');

const { pluginName } = Config.get();

// string containing the '/node_modules/'
const nodeModuleDirname = path.sep + 'node_modules' + path.sep;
Expand Down
10 changes: 6 additions & 4 deletions src/Loader/Messages/Exeptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ const makeSerializable = require('webpack/lib/util/makeSerializable');

const ansis = require('ansis');
const { red, yellow, cyan, green, ansi256, cyanBright, reset, whiteBright, bgYellow } = require('ansis');
const { pluginName } = require('../../config');
const Config = require('../../Common/Config');

const { pluginLabel } = Config.get();

const redBright = ansi256(203);
const pluginHeaderHtml = `<span style="color:#e36049">[${pluginName}]</span>`;
const pluginHeaderHtml = `<span style="color:#e36049">[${pluginLabel}]</span>`;

class LoaderException extends WebpackError {
error = null;
Expand All @@ -20,7 +22,7 @@ class LoaderException extends WebpackError {
* @param {Error?} error The original error.
*/
constructor(message, error) {
message = `\n${reset.whiteBright.bgRedBright` ${pluginName} `} ${whiteBright(message)}\n`;
message = `\n${reset.whiteBright.bgRedBright` ${pluginLabel} `} ${whiteBright(message)}\n`;

if (error && error.stack) {
message += error.stack;
Expand Down Expand Up @@ -63,7 +65,7 @@ class LoaderException extends WebpackError {
* @returns {string}
*/
const errorToHtml = (error) => {
let message = ansis.strip(error.toString()).replace(pluginName, pluginHeaderHtml).replace(/\n/g, '<br>');
let message = ansis.strip(error.toString()).replace(pluginLabel, pluginHeaderHtml).replace(/\n/g, '<br>');

return `<!DOCTYPE html><html><head></head><body>${message}</body></html>`;
};
Expand Down
25 changes: 21 additions & 4 deletions src/Plugin/AssetCompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const AssetGenerator = require('webpack/lib/asset/AssetGenerator');
//const JavascriptParser = require('webpack/lib/javascript/JavascriptParser');
//const JavascriptGenerator = require('webpack/lib/javascript/JavascriptGenerator');

const { pluginName } = require('../config');
const Config = require('../Common/Config');
const { baseUri, urlPathPrefix, cssLoaderName } = require('../Loader/Utils');
const { findRootIssuer } = require('../Common/CompilationHelpers');
const { isDir } = require('../Common/FileUtils');
Expand All @@ -37,6 +37,7 @@ const { compilationName, verbose } = require('./Messages/Info');
const { PluginError, afterEmitException } = require('./Messages/Exception');

const loaderPath = require.resolve('../Loader');
const { pluginName } = Config.get();

/**
* The CSS loader.
Expand Down Expand Up @@ -181,7 +182,22 @@ class AssetCompiler {
* @param {Compiler} compiler The instance of the webpack compiler.
* @abstract
*/
initialize(compiler) {}
init(compiler) {}

/**
* Initialize loader for entry files.
*/
initLoader() {
const defaultLoader = {
test: Option.get().test,
// ignore 'asset/source' with the '?raw' query
// see https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax
resourceQuery: { not: [/raw/] },
loader: loaderPath,
};

Option.addLoader(defaultLoader);
}

/**
* Apply plugin.
Expand All @@ -194,6 +210,7 @@ class AssetCompiler {
const { webpack } = compiler;
const { NormalModule, Compilation } = webpack;

this.promises = [];
this.fs = compiler.inputFileSystem.fileSystem;
this.webpack = webpack;
HotUpdateChunk = webpack.HotUpdateChunk;
Expand All @@ -204,8 +221,8 @@ class AssetCompiler {
Option.enableLibraryType(this.entryLibrary.type);
AssetResource.init(compiler);

this.initialize(compiler);
this.promises = [];
this.init(compiler);
this.initLoader();

// initialize integrity plugin
this.integrityPlugin = new Integrity(Option);
Expand Down
6 changes: 4 additions & 2 deletions src/Plugin/Messages/Deprecation.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const { red, green, black } = require('ansis');
const { pluginName } = require('../../config');
const { outToConsole } = require('../../Common/Helpers');
const Config = require('../../Common/Config');

const header = `\n${black.bgYellow` ${pluginName} `}${black.bgAnsi(227)` DEPRECATE `} `;
const { pluginLabel } = Config.get();

const header = `\n${black.bgYellow` ${pluginLabel} `}${black.bgAnsi(227)` DEPRECATE `} `;

// Example for deprecations

Expand Down
5 changes: 3 additions & 2 deletions src/Plugin/Messages/Exception.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const fs = require('fs');
const path = require('path');
const { reset, green, cyan, cyanBright, yellow, white, whiteBright, redBright } = require('ansis');
const { pluginName } = require('../../config');
const Config = require('../../Common/Config');

const { pluginLabel } = Config.get();
const PluginError = new Set();

class PluginException extends Error {
Expand All @@ -11,7 +12,7 @@ class PluginException extends Error {
* @param {Error?} error The original error.
*/
constructor(message, error) {
message = `\n${reset.whiteBright.bgRedBright` ${pluginName} `} ${whiteBright(message)}\n`;
message = `\n${reset.whiteBright.bgRedBright` ${pluginLabel} `} ${whiteBright(message)}\n`;

if (error && error.stack) {
message += error.stack;
Expand Down
10 changes: 6 additions & 4 deletions src/Plugin/Messages/Info.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ const {
const Collection = require('../Collection');
const { outToConsole, isFunction } = require('../../Common/Helpers');
const { relativePathForView } = require('../../Common/FileUtils');
const { pluginName } = require('../../config');
const Config = require('../../Common/Config');

const Dependency = require('../../Loader/Dependency');
const PluginService = require('../PluginService');

const { pluginLabel } = Config.get();

const gray = ansi256(244);
const padLevel1 = 16;
const padLevel2 = padLevel1 + 10;
Expand All @@ -36,8 +38,8 @@ const padChunks = padLevel1 + 4;
*/
const compilationName = (error) =>
error
? bgAnsi(196).whiteBright` HTML Bundler Plugin ` + red` ▶▶▶`
: bgAnsi(118).black` HTML Bundler Plugin ` + green` ▶▶▶`;
? bgAnsi(196).whiteBright` ${pluginLabel} ` + red` ▶▶▶`
: bgAnsi(118).black` ${pluginLabel} ` + green` ▶▶▶`;

const colorType = (item, pad) => {
let { type, inline } = item;
Expand Down Expand Up @@ -100,7 +102,7 @@ const renderAssets = (item, pad = padLevel2) => {
* Display all processed assets in entry points.
*/
const verbose = () => {
let str = '\n' + black.bgGreen` ${pluginName} ` + bgAnsi256(193).black` Entry processing ` + '\n';
let str = '\n' + black.bgGreen` ${pluginLabel} ` + bgAnsi256(193).black` Entry processing ` + '\n';

// display loader watch dependencies
if (PluginService.isWatchMode()) {
Expand Down
3 changes: 0 additions & 3 deletions src/Plugin/Option.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ const path = require('path');
const { isWin, isFunction, pathToPosix } = require('../Common/Helpers');
const LoaderOption = require('../Loader/Option');
const { postprocessException, beforeEmitException } = require('./Messages/Exception');

const pluginName = require('../config');
const Preprocessor = require('../Loader/Preprocessor');

class Option {
static pluginName = pluginName;
/** @type {HtmlBundlerPlugin.PluginOptions} */
static options = {};
/** @type {AssetEntry} */
Expand Down
9 changes: 6 additions & 3 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const pluginName = 'html-bundler-webpack-plugin';

module.exports = {
pluginName,
// plugin name, must be same as node module name
pluginName: 'html-bundler-webpack-plugin',
// label in terminal output from plugin
pluginLabel: 'HTML Bundler Plugin',
// label in terminal output from loader
loaderLabel: 'HTML Bundler Loader',
};
19 changes: 7 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const Option = require('./Plugin/Option');
const Config = require('./Common/Config');

// Note: init config before import any source file
Config.init('./config.js');

const AssetCompiler = require('./Plugin/AssetCompiler');
const loader = require.resolve('./Loader');

Expand Down Expand Up @@ -37,17 +41,8 @@ class Plugin extends AssetCompiler {
*
* @param {Compiler} compiler The instance of the webpack compilation.
*/
initialize(compiler) {
// Note: the default templating engine is Eta.
const defaultLoader = {
test: Option.get().test,
// ignore 'asset/source' with the '?raw' query
// see https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax
resourceQuery: { not: [/raw/] },
loader,
};

Option.addLoader(defaultLoader);
init(compiler) {
// TODO: do some thing in an extended plugin
}
}

Expand Down
Loading

0 comments on commit b156817

Please sign in to comment.