Security News
The Risks of Misguided Research in Supply Chain Security
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
pug-plugin
Advanced tools
Pug plugin for webpack compiles Pug files to HTML, extracts CSS and JS from their sources specified in Pug.
Pug Plugin enable to use Pug files in Webpack entry and generates HTML files that contain the hashed output JS and CSS filenames whose source files are specified in the Pug template.
💡 Highlights:
:escape
:code
:highlight
:markdown
.Specify the Pug files in the Webpack entry:
const PugPlugin = require('pug-plugin');
module.exports = {
entry: {
// define your Pug files here
index: './src/views/home/index.pug', // output dist/index.html
about: './src/views/about/index.pug', // output dist/about.html
},
plugins: [
new PugPlugin(), // enable rendering of Pug files defined in Webpack entry
],
module: {
rules: [
{
test: /.pug$/,
loader: PugPlugin.loader, // Pug loader
},
],
},
};
Note
The key of
entry
object is a relative output file w/o extension.html
in output path.
The defaultoutput.path
ispath.join(__dirname, 'dist')
.
The defaultoutput.filename
is[name].js
.
Add source scripts and styles directly to Pug using require()
:
link(href=require('./styles.scss') rel='stylesheet')
script(src=require('./main.js') defer='defer')
Generated HTML contains hashed CSS and JS output filenames:
<link href="/assets/css/styles.05e4dd86.css" rel="stylesheet">
<script src="/assets/js/main.f4b855d8.js" defer="defer"></script>
Just one Pug plugin replaces the functionality of many plugins and loaders used with Pug:
Package | Features |
---|---|
html-webpack-plugin | extract HTML and save in a file |
mini-css-extract-plugin | extract CSS and save in a file |
webpack-remove-empty-scripts | remove empty JS files generated by the mini-css-extract-plugin |
resolve-url-loader | resolve url in CSS |
svg-url-loader | encode SVG data-URL as utf8 |
posthtml-inline-svg | inline SVG icons in HTML |
pug-loader | the Pug loader is already included in the Pug plugin |
Warning
Don't use the
pug-plugin
together withhtml-webpack-plugin
andmini-css-extract-plugin
.
Thepug-plugin
is designed to replace these plugins to make Pug easier to use and faster to compile.
The fundamental difference between mini-css-extract-plugin
and pug-plugin
:
mini-css-extract-plugin
extracts CSS from styles imported in JavaScript
and require additional html-webpack-plugin
to inject <link>
tag somewhere in any HTML file.pug-plugin
extracts CSS from styles specified directly in Pug and save into separate file under hashed output filename.
The <link>
tag stays in place and href
will contain output hashed filename.
You can specify <link>
tag exactly where you want in HTML.
The Pug plugin extracts CSS much faster because it is optimized to perform the same works in one step.node_modules
in Pugnode_module
in SCSSsplitChunks
auto
publicPathpretty
formatting of generated HTMLasset/resource
asset/inline
asset
base64 encoding
of binary images as data-URL in HTML and CSSinline CSS
in HTMLinline SVG
in HTMLinline SVG
as data-URL in CSS
background: url('./icons/iphone.svg') // CSS: url("data:image/svg+xml,<svg>...</svg>")
*.LICENSE.txt
filepostprocess
for modules to handle the extracted contentInstall the pug-plugin
:
npm install pug-plugin --save-dev
Install additional packages for styles:
npm install css-loader sass sass-loader --save-dev
Change your webpack.config.js
according to the following minimal configuration:
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
publicPath: '/',
// output filename of JS files
filename: 'assets/js/[name].[contenthash:8].js',
},
entry: {
// don't define scripts and styles in the Webpack entry,
// all scripts and styles must be specified in Pug
// define Pug files in entry:
index: './src/views/index.pug', // output index.html
about: './src/views/about/index.pug', // output about.html
// ...
},
plugins: [
// enable using Pug files as entry point
new PugPlugin({
pretty: true, // formatting HTML, should be used in development mode only
extractCss: {
// output filename of CSS files
filename: 'assets/css/[name].[contenthash:8].css',
},
}),
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader, // the Pug loader
},
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader'],
},
],
},
};
Note
The automatic public path is supported, but for faster compilation is recommended to use a fixed
publicPath
(e.g.'/'
or''
).
Pug template src/views/index.pug
:
html
head
//- add styles in head
link(href=require('./styles.scss') rel='stylesheet')
body
h1 Hello Pug!
//- add scripts at last position in body
script(src=require('./main.js'))
Generated HTML:
<html>
<head>
<link href="/assets/css/styles.f57966f4.css" rel="stylesheet">
</head>
<body>
<h1>Hello Pug!</h1>
<script src="/assets/js/main.b855d8f4.js"></script>
</body>
</html>
Warning
- Don't define styles and JS files in entry. You can use
require()
for source files of JS and SCSS in Pug.- Don't import styles in JS. Use
require()
for style files in Pug.- Don't use
html-webpack-plugin
to render Pug files in HTML. The Pug plugin processes files from Webpack entry.- Don't use
mini-css-extract-plugin
to extract CSS from styles. The Pug plugin extract CSS from styles required in Pug.
enabled
Type: boolean
Default: true
Enable/disable the plugin.
verbose
Type: boolean
Default: false
Show the file information at processing of entry.
pretty
Type: boolean
Default: false
Pretty formatting the resulting HTML. Use this option for debugging only. For production build should be disabled.
This option only works for Pug files defined in the Webpack entry.
Warning️
The
pretty
option of thepug-loader
is deprecated, therefore use thepretty
option inpug-plugin
.
const PugPlugin = require('pug-plugin');
module.exports = {
plugins: [
new PugPlugin({
pretty: true, // enable formatting of HTML
}),
],
};
test
Type: RegExp
Default: /\.pug$/
Use the test
to match module options by source filename of a resource.
For example, save all extracted svg
files from fonts/
to the separate output directory:
const PugPlugin = require('pug-plugin');
module.exports = {
plugins: [
new PugPlugin({
modules: [
{
test: /fonts\/.+\.svg$/,
outputPath: path.join(__dirname, 'dist/some/other/path/'),
},
],
}),
],
};
sourcePath
Type: string
Default: webpack.options.context
The absolute path to sources.
outputPath
Type: string
Default: webpack.options.output.path
The output directory for processed entries. This directory can be relative by webpack.options.output.path
or absolute.
filename
Type: string | Function
Default: webpack.output.filename || '[name].html'
The name of output file.
string
then following substitutions (see output.filename for chunk-level) are available in template string:
[id]
The ID of the chunk.[name]
Only filename without extension or path.[contenthash]
The hash of the content.[contenthash:nn]
The nn
is the length of hashes (defaults to 20).Function
then following arguments are available in the function:
@param {PathData} pathData
has the useful properties (see the type PathData):
pathData.filename
the full path to source filepathData.chunk.name
the name of entry key@param {AssetInfo} assetInfo
Mostly this object is empty.@return {string}
The name or template string of output file.modules
Type: ModuleOptions[]
Default: []
The array of objects of type ModuleOptions
to separately handles of different file types.
The description of @property
see by Pug plugin options.
/**
* @typedef {Object} ModuleOptions
* @property {boolean} enabled
* @property {boolean} verbose
* @property {RegExp} test
* @property {string} sourcePath
* @property {string} outputPath
* @property {string | function(PathData, AssetInfo): string} filename
* @property {function(string, ResourceInfo, Compilation): string | null} postprocess
*/
extractCss
Type: ModuleOptions
Default module options:
{
test: /\.(css|scss|sass|less|styl)$/,
enabled: true,
verbose: false,
filename: '[name].css',
sourcePath: null,
outputPath: null,
}
The options for embedded extractCss
module. This module extracts CSS from style sources specified in Pug using require()
, e.g.:
link(href=require('./styles.scss') rel='stylesheet')
Warning
Don't import source styles in JavaScript! Styles must be specified directly in Pug.
The default CSS output filename is [name].css
. You can specify your own filename using webpack filename substitutions:
const PugPlugin = require('pug-plugin');
module.exports = {
plugins: [
new PugPlugin({
extractCss: {
filename: 'assets/css/[name].[contenthash:8].css',
},
}),
],
};
The name
is the filename of required style source.
For example, if source file is styles.scss
, then output filename will be assets/css/styles.1234abcd.css
.
If you want to have a different output filename, you can use the filename
options as the function.
Note
Since version
3.0.0
the moduleextractCss
is enabled by default.
Warning
Don't use
mini-css-extract-plugin
orstyle-loader
, they are not required more.
TheextractCss
module extracts CSS much faster than other plugins and resolves all asset URLs in CSS, therefore theresolve-url-loader
is redundant too.
extractComments
Type: boolean
Default: false
Enable / disable extraction of comments to *.LICENSE.txt
file.
When used splitChunks
optimization for node modules that contains comments,
then Webpack will extract this comments in separate text file.
Defaults, Pug plugin prevent to create such needless files.
But if you want to extract files like *.LICENSE.txt
then set this option to true
:
const PugPlugin = require('pug-plugin');
module.exports = {
plugins: [
new PugPlugin({
extractComments: true,
}),
],
};
postprocess
Type: Function
Default: null
The post process for extracted content from compiled entry.
The following parameters are available in the function:
@param {string} content
The content of compiled entry.@param {ResourceInfo} info
The info of current asset.@param {webpack Compilation} compilation
The Webpack compilation object.@return {string | null}
Return string content to save to output directory.null
then the compiled content of the entry will be ignored, and will be saved original content compiled as JS module.
Returning null
can be useful for debugging to see the source of the compilation of the Webpack loader./**
* @typedef {Object} ResourceInfo
* @property {boolean} [verbose = false] Whether information should be displayed.
* @property {boolean} isEntry True if is the asset from entry, false if asset is required from pug.
* @property {string | (function(PathData, AssetInfo): string)} filename The filename template or function.
* @property {string} sourceFile The absolute path to source file.
* @property {string} outputPath The absolute path to output directory of asset.
* @property {string} assetFile The output asset file relative by outputPath.
*/
The Pug plugin contain the pug-loader. Complete description see under pug-loader options.
method
Type: string
Default: render
Note
The default method of
pug-loader
iscompile
, but using thepug-plugin
the default loader method isrender
, because the plugin renders Pug to static HTML and this method is fastest.
render
The render
method renders Pug into HTML at compile time and exports the HTML as a string.
Add to Webpack config the module rule:
const PugPlugin = require('pug-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader, // default method is 'render'
},
],
},
};
See the example code.
compile
The compile
method compiles Pug into a template function and in JavaScript can be called with variables to render into HTML at runtime.
To use the render
method for rendering Pug from the Webpack entry and the compile
method in JavaScript, use the oneOf
Webpack rule:
const PugPlugin = require('pug-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.pug$/,
oneOf: [
// import Pug in JavaScript/TypeScript as template function
{
issuer: /\.(js|ts)$/, // match scripts where Pug is used
loader: PugPlugin.loader,
options: {
method: 'compile', // compile Pug into template function
},
},
// render Pug from Webpack entry into static HTML
{
loader: PugPlugin.loader, // default method is 'render'
},
],
},
],
},
};
See the example code.
The simple example of resolving the asset resources via require() in Pug and via url() in scss.
The Webpack config:
const PugPlugin = require('pug-plugin');
module.exports = {
entry: {
index: './src/pages/home/index.pug', // one entry point for all assets
// ... here can be defined many Pug templates
},
output: {
path: path.join(__dirname, 'dist/'),
publicPath: '/',
// output filename of JS files
filename: 'assets/js/[name].[contenthash:8].js'
},
resolve: {
alias: {
// use alias to avoid relative paths like `./../../images/`
Images: path.join(__dirname, './src/images/'),
Fonts: path.join(__dirname, './src/fonts/')
}
},
plugins: [
new PugPlugin({
extractCss: {
// output filename of CSS files
filename: 'assets/css/[name].[contenthash:8].css'
},
}),
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
},
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader']
},
{
test: /\.(png|jpg|jpeg|ico)/,
type: 'asset/resource',
generator: {
// output filename of images
filename: 'assets/img/[name].[hash:8][ext]',
},
},
{
test: /\.(woff|woff2|eot|ttf|otf|svg)$/i,
type: 'asset/resource',
generator: {
// output filename of fonts
filename: 'assets/fonts/[name][ext][query]',
},
},
],
},
};
The Pug template ./src/pages/home/index.pug
:
html
head
link(rel="icon" type="image/png" href=require('Images/favicon.png'))
link(href=require('./styles.scss') rel='stylesheet')
body
.header Here is the header with background image
h1 Hello Pug!
img(src=require('Images/pug-logo.jpg') alt="pug logo")
script(src=require('./main.js'))
The source script ./src/pages/home/main.js
console.log('Hello Pug!');
The source styles ./src/pages/home/styles.scss
// Pug plugin can resolve styles in node_modules.
// The package 'material-icons' must be installed in packages.json.
@use 'material-icons';
// Resolve the font in the directory using the Webpack alias.
@font-face {
font-family: 'Montserrat';
src: url('Fonts/Montserrat/Montserrat-Regular.woff2'); // pug-plugin can resolve url
...
}
body {
font-family: 'Montserrat', serif;
}
.header {
background-image: url('Images/header.png'); // pug-plugin can resolve url
...
}
Note
The Pug plugin can resolve an url (as relative path, with alias, from node_modules) without requiring
source-maps
. Do not need additional loader such asresolve-url-loader
.
The generated CSS dist/assets/css/styles.f57966f4.css
:
/*
* All styles of npm package 'material-icons' are included here.
* The imported fonts from `node_modules` will be coped in output directory.
*/
@font-face {
font-family: "Material Icons";
src:
url(/assets/fonts/material-icons.woff2) format("woff2"),
url(/assets/fonts/material-icons.woff) format("woff");
...
}
.material-icons {
font-family: "Material Icons";
...
}
/*
* Fonts from local directory.
*/
@font-face {
font-family: 'Montserrat';
src: url(/assets/fonts/Montserrat-Regular.woff2);
...
}
body {
font-family: 'Montserrat', serif;
}
.header {
background-image: url(/assets/img/header.4fe56ae8.png);
...
}
Note
All resolved files will be coped to the output directory, so no additional plugin is required, such as
copy-webpack-plugin
.
The generated HTML dist/index.html
contains the hashed output filenames of the required assets:
<html>
<head>
<link rel="stylesheet" href="/assets/css/styles.f57966f4.css">
</head>
<body>
<div class="header">Here is the header with background image</div>
<h1>Hello Pug!</h1>
<img src="/assets/img/pug-logo.85e6bf55.jpg" alt="pug logo">
<script src="/assets/js/main.b855d8f4.js"></script>
</body>
</html>
All this is done by one Pug plugin, without additional plugins and loaders. To save build time, to keep your Webpack config clear and clean, just use this plugin.
render
method./webpack.config.js
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
filename: '[name].[contenthash:8].js',
},
entry: {
index: './src/index.pug' // Pug plugin generates dist/index.html
},
plugins: [
new PugPlugin(),
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
},
],
},
};
./src/index.pug
html
head
body
h1 Hello Pug!
#js-component-container
script(src=require('./component.js'))
./src/component.pug
.component
h1 Title
p The teaser.
./src/component.js
import componentHtml from './component.pug';
const containerElm = document.getElementById('js-component-container');
containerElm.innerHTML = componentHtml;
The componentHtml
contain rendered HTML string:
<div class="component">
<h1>Title</h1>
<p>The teaser.</p>
</div>
The generated ./dist/index.html
:
<html>
<head></head>
<body>
<h1>Hello Pug!</h1>
<div id="js-component-container">
<!-- The Pug component inserted in JavaScript -->
<div class="component">
<h1>Title</h1>
<p>The teaser.</p>
</div>
</div>
<script src='component.b855d8f4.js'></script>
</body>
</html>
compile
method./webpack.config.js
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
filename: '[name].[contenthash:8].js',
},
entry: {
index: './src/index.pug' // Pug plugin generates dist/index.html
},
plugins: [
new PugPlugin(),
],
module: {
rules: [
{
test: /\.pug$/,
oneOf: [
// import Pug in JavaScript/TypeScript as template function
{
issuer: /\.(js|ts)$/,
loader: PugPlugin.loader,
options: {
method: 'compile',
},
},
// render Pug from Webpack entry into static HTML
{
loader: PugPlugin.loader,
},
],
},
],
},
};
./src/index.pug
html
head
body
h1 Hello Pug!
#js-component-container
script(src=require('./component.js'))
./src/component.pug with variables
.component
h1 #{title}
p #{teaser}
./src/component.js
import componentTmpl from './component.pug';
const componentHtml = componentTmpl({
title: 'My component',
teaser: 'My teaser.'
});
const containerElm = document.getElementById('js-component-container');
containerElm.innerHTML = componentHtml;
The componentTmpl
contain the template function.
The componentHtml
contain rendered HTML string with passed data at runtime:
<div class="component">
<h1>My component</h1>
<p>My teaser.</p>
</div>
The generated ./dist/index.html
:
<html>
<head></head>
<body>
<h1>Hello Pug!</h1>
<div id="js-component-container">
<!-- The Pug component with variables passed in JavaScript -->
<div class="component">
<h1>My component</h1>
<p>My teaser.</p>
</div>
</div>
<script src='component.b855d8f4.js'></script>
</body>
</html>
There are two ways to keep/change the output filename for Pug files:
filename
property as a Function like keepPugFolderStructure()
in the example belowconst path = require('path');
const PugPlugin = require('pug-plugin');
const sourcePath = path.join(__dirname, 'src'); // => /path/to/src
const keepPugFolderStructure = (pathData) => {
const sourceFile = pathData.filename; // => /path/to/src/pages/about.pug
const relativeFile = path.relative(sourcePath, sourceFile); // => pages/about.pug
const { dir, name } = path.parse(relativeFile); // dir: 'pages', name: 'about'
return `${dir}/${name}.html`; // => dist/pages/about.html
};
module.exports = {
entry: {
index: './src/index.pug', // dist/index.html
'pages/contact': './src/pages/contact/index.pug', // dist/pages/contact.html
// any unique key, not used to generate the output filename
page001: {
import: './src/pages/about.pug',
filename: keepPugFolderStructure, // => dist/pages/about.html
},
},
plugins: [new PugPlugin()],
module: {
rules: [
{
test: /\.(pug)$/,
loader: PugPlugin.loader,
},
],
},
};
To keep/change the output filename for all Pug files, use the filename
option of the Pug plugin as a Function like keepPugFolderStructure()
in the example:
const path = require('path');
const PugPlugin = require('pug-plugin');
const sourcePath = path.join(__dirname, 'src'); // => /path/to/src
const keepPugFolderStructure = (pathData) => {
const sourceFile = pathData.filename; // => /path/to/src/pages/about.pug
const relativeFile = path.relative(sourcePath, sourceFile); // => pages/about.pug
const { dir, name } = path.parse(relativeFile); // dir: 'pages', name: 'about'
return `${dir}/${name}.html`; // => dist/pages/about.html
};
module.exports = {
entry: {
// Note: each key must be unique, not used to generate the output filename.
// The output filename will be generated by source filename via the keepPugFolderStructure().
page001: './src/index.pug', // => dist/index.html
page002: './src/pages/about.pug', // => dist/pages/about.html
page003: './src/pages/contact/index.pug', // => dist/pages/contact/index.html
},
plugins: [
new PugPlugin({
// use the function to dynamic generate output filenames for all Pug files defined in the entry
filename: keepPugFolderStructure,
}),
],
module: {
rules: [
{
test: /\.(pug)$/,
loader: PugPlugin.loader,
},
],
},
};
To keep/change the output filename for all asset resources, like fonts, use the generator.filename
as a Function, for example:
const path = require('path');
const PugPlugin = require('pug-plugin');
const sourceDirname = 'src/';
module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2|svg|eot|ttf|otf)$/,
include: /[\\/]fonts[\\/]/, // match SVG font only from '/fonts/' directory
type: 'asset/resource',
generator: {
filename: (pathData) => {
const { dir } = path.parse(pathData.filename); // the filename is relative path by project
const outputPath = dir.replace(sourceDirname, '');
return outputPath + '/[name][ext]';
},
},
},
],
},
};
The source font files:
src/assets/fonts/OpenSans/open-sans-italic.svg
src/assets/fonts/OpenSans/open-sans-regular.svg
The font files in output dist/
directory will have original folder structure:
dist/assets/fonts/OpenSans/open-sans-italic.svg
dist/assets/fonts/OpenSans/open-sans-regular.svg
Many node modules specify compiled bundles for the browser in fields of its own package.json
.
For example, the material-icons use the field browser
for compiled CSS file.
The bootstrap use the main
field for compiled JS and the style
field for CSS.
You can specify only module name, Pug plugin automatically resolves files for script and style:
link(href=require('bootstrap') rel='stylesheet') // bootstrap/dist/css/bootstrap.css
script(src=require('bootstrap')) // bootstrap/dist/js/bootstrap.js
If you need to load a specific version of a file, use the path to that file, for example:
link(href=require('bootstrap/dist/css/bootstrap.rtl.css') rel='stylesheet')
script(src=require('bootstrap/dist/js/bootstrap.bundle.js'))
Warning
Don't use a relative path to
node_modules
, like../../../node_modules/bootstrap
. The Pug plugin resolves node modules by their name.
Pug plugin can resolve styles in node_modules
.
Note
Pug plugin resolves styles much fasted than the resolve-url-loader and don't require to use the source map in
sass-loader
.
@use 'MODULE_NAME/path/to/style';
Important: the file extension, e.g. .scss, .css, must be omitted.
Example how to import source styles of material-icons:
// import styles from installed module `material-icons`
@use 'material-icons';
// define short class name
.mat-icon {
@extend .material-icons-outlined;
}
Usage of the icon home
in Pug:
.mat-icon home
Example how to import the style theme tomorrow
of the prismjs module:
// import default prismjs styles from installed module `prismjs`
@use 'prismjs/themes/prism-tomorrow.min';
Note
use the
@use
instead of@import
, because it is deprecated.
To inline CSS extracted from style source in HTML use the ?inline
URL query.
Webpack config rule for styles
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader'],
},
styles.scss
$color: crimson;
h1 {
color: $color;
}
Pug
html
head
// load CSS as file
link(href=require('./main.scss') rel='stylesheet')
// inline CSS from styles.scss
style=require('./styles.scss?inline')
body
h1 Hello World!
Generated HTML
<html>
<head>
<!-- load CSS as file -->
<link href="assets/css/main.05e4dd86.css" rel="stylesheet">
<!-- inline CSS from styles.scss -->
<style>
h1 {
color: crimson;
}
</style>
</head>
<body>
<h1>Hello Pug!</h1>
</body>
</html>
Warning
Don't use
@import in CSS
. It's verybad practice
.
Bad example:
main.css
@import 'path/to/style.css';
Pug plugin not support handling of @import url
in CSS. Imported url will be passed 1:1 into resulting CSS.
The problem: defaults, css-loader
handles @import at-rule, which causes an issue in the Pug plugin.
To avoid this problem add the import: false
option to css-loader
to disable handling of @import at-rule in CSS:
{
test: /.(css)$/i,
use: [
{
loader: 'css-loader',
options: {
import: false, // pass @import url as is
},
},
],
},
Note
Because imported in CSS files are not handled, these files need to be manually copied to a
dist
folder using thecopy-webpack-plugin
.
splitChunks
Defaults, Webpack will split every entry module. Because the entry point is Pug files, Webpack tries to split those files too, which completely breaks the compilation process in pug-plugin.
To avoid this issue, you must specify which scripts should be split, using optimization.splitChunks.cacheGroups
:
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
scripts: {
test: /\.(js|ts)$/,
chunks: 'all',
},
},
},
},
// ...
};
Note
In the
test
option must be specified all extensions of scripts which should be split.
See details by splitChunks.cacheGroups.
When used splitChunks optimization for script and style node modules specified in Pug, for example:
html
head
link(href=require('bootstrap/dist/css/bootstrap.min.css') rel='stylesheet')
script(src=require('bootstrap/dist/js/bootstrap.min.js') defer)
body
h1 Hello Pug!
script(src=require('./main.js'))
In this use case the optimization.cacheGroups.{cacheGroup}.test
option must match exactly only JS files from node_modules
:
module.exports = {
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].+\.(js|ts)$/, // use exactly this Regexp
name: 'vendor',
chunks: 'all',
},
},
},
},
// ...
};
Warning
Splitting CSS to many chunk is principal impossible. Splitting works only for JS files. If you use vendor styles in your style file, e.g.:
styles.scss
@use "bootstrap/scss/bootstrap"; body { color: bootstrap.$primary; }
Then vendor styles will not be saved to a separate file, because
sass-loader
generates one CSS bundle code. Therefore vendor styles should be loaded in Pug separately.
Warning
If you will to use the
test
as/[\\/]node_modules[\\/]
, without extension specification, then Webpack concatenates JS code together with CSS in one file, because Webpack can't differentiate CSS module from JS module, therefore you MUST match only JS files.If you want save module styles separate from your styles, then load them in Pug separately:
html head //- require module styles separately: link(href=require('bootstrap/dist/css/bootstrap.min.css') rel='stylesheet') //- require your styles separately: link(href=require('./styles.scss') rel='stylesheet') body h1 Hello Pug! script(src=require('./main.js'))
If you use many node modules and want save each module to separate file then use optimization.cacheGroups.{cacheGroup}.name
as function.
For example, you imports many node modules in the script.js
:
import { Button } from 'bootstrap';
import _, { map } from 'underscore';
// ..
Then, use the name
as following function:
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
filename: 'js/[name].[contenthash:8].js', // output filename of JS files
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
minSize: 10000, // extract modules bigger than 10KB, defaults is 30KB
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].+\.(js|ts)$/, // split JS only, ignore CSS modules
// save node module under own name
name(module) {
const name = module.resourceResolveData.descriptionFileData.name.replace('@', '');
return `npm.${name}`;
},
},
},
},
},
// ...
};
The split files will be saved like this:
dist/js/npm.popperjs/core.f96a1152.js <- the `popperjs/core` used in bootstrap will be extracted too
dist/js/npm.bootstrap.f69a4e44.js
dist/js/npm.underscore.4e44f69a.js
dist/js/runtime.9cd0e0f9.js <- common runtime code
dist/js/script.3010da09.js
To enable live reload by changes any file add in the Webpack config the devServer
option:
module.exports = {
// enable HMR with live reload
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
watchFiles: {
paths: ['src/**/*.*'],
options: {
usePolling: true,
},
},
},
};
Note
Live reload works only if in Pug used a JS file. If your Pug template has not a JS, then create one empty JS file, e.g.
hmr.js
and add it in Pug fordevelopment
mode only:
if isDev script(src=require('./hmr.js'))
Where
isDev
is the passed variable from Webpack config.
To pass global variables into all Pug files, add a variable in the data
option of PugPlugin.loader
:
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
mode: isDev ? 'development' : 'production',
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
options: {
data: {
isDev, // pass global variable into all Pug files
}
},
},
],
},
};
npm run test
will run the unit and integration tests.
npm run test:coverage
will run the tests with coverage.
PugPlugin.loader
:highlight
filter highlights code syntax:markdown
filter transform markdown to HTML and highlights code syntax4.5.1 (2022-12-26)
data
in loader option when the self
loader option is true in the compile
methodFAQs
Pug plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in Pug.
The npm package pug-plugin receives a total of 3,187 weekly downloads. As such, pug-plugin popularity was classified as popular.
We found that pug-plugin demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.