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 to extract html, css and js from pug templates defined in webpack entry.
The pug plugin enable using Pug templates as webpack entry points. This plugin extract HTML, JavaScript and CSS from pug files defined in webpack entry.
Now is possible to define pug templates in webpack entry. All styles and scripts will be automatically extracted from pug.
const PugPlugin = require('pug-plugin');
module.exports = {
entry: {
'index': './src/index.pug',
},
plugins: [
new PugPlugin(),
],
// ...
};
Now is possible to use the source files of styles and scripts directly in pug.
link(href=require('./styles.scss') rel='stylesheet')
script(src=require('./main.js'))
The generated HTML contains hashed CSS and JS output filenames, depending on how webpack is configured.
<link rel="stylesheet" href="/assets/css/styles.05e4dd86.css">
<script src="/assets/js/main.f4b855d8.js"></script>
💡 The required styles and scripts in pug do not need to define in the webpack entry. All required resources in pug will be automatically handled by webpack.
The single pug plugin perform the most commonly used functions of the following packages:
Packages | Features |
---|---|
html-webpack-plugin | extract HTML |
mini-css-extract-plugin | extract CSS |
webpack-remove-empty-scripts | remove empty js files generated by the mini-css-extract-plugin |
resolve-url-loader | resolve url in CSS |
pug-loader | the pug loader is already included in the pug plugin |
You can replace all of the above packages with just one pug plugin.
Install the pug-plugin
.
npm install pug-plugin --save-dev
Install additional packages.
npm install css-loader sass sass-loader --save-dev
The minimal webpack config to extract HTML, CSS and JS from pug:
const PugPlugin = require('pug-plugin');
module.exports = {
entry: {
// all scripts and styles can be used directly in pug,
// do not need to define js and scss in the webpack entry
index: './src/index.pug' // output index.html
},
output: {
path: path.join(__dirname, 'dist/'),
publicPath: '/',
// output filename for JS files
filename: 'assets/js/[name].[contenthash:8].js'
},
plugins: [
new PugPlugin({
modules: [
PugPlugin.extractCss({
// output filename for CSS files
filename: 'assets/css/[name].[contenthash:8].css'
})
]
})
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
options: {
method: 'render', // fastest build method
}
},
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader']
},
],
},
};
The pug template src/index.pug
:
html
head
link(rel='stylesheet' href=require('./styles.scss'))
body
h1 Hello Pug!
script(src=require('./main.js'))
The generated HTML:
<html>
<head>
<link rel="stylesheet" href="/assets/css/styles.f57966f4.css">
</head>
<body>
<p>Hello Pug!</p>
<script src="/assets/js/main.b855d8f4.js"></script>
</body>
</html>
pug
files defined in webpack entry
into separate filerequire()
and replace the source filename with a output filename.html
files defined in webpack entry
without additional plugins like html-webpack-plugin
module.exports = {
entry: {
index: './src/index.html', // save the HTML to output directory as `index.html`
},
}
webpack entry
without additional plugins like mini-css-extract-plugin
module.exports = {
entry: {
styles: './src/assets/scss/main.scss', // extract CSS and save to output directory as `styles.css`
},
}
@use 'material-icons'; /* <= resolve urls in the imported node module */
@font-face {
font-family: 'Montserrat';
src:
url('../fonts/Montserrat-Regular.woff') format('woff'), /* <= resolve url relative by source */
url('../fonts/Montserrat-Regular.ttf') format('truetype');
}
.logo {
background-image: url("~Images/logo.png"); /* <= resolve url by webpack alias */
}
⚠️ Avoid using resolve-url-loader together with
PugPlugin.extractCss
because theresolve-url-loader
is buggy, in some cases fails to resolve an url. The pug plugin resolves all urls well and much faster thanresolve-url-loader
. Unlikeresolve-url-loader
, this plugin resolves an url without requiring source-maps. see test case to resolve url
webpack entry
syntax to define source / output files separately for each entry
module.exports = {
entry: {
about: { import: './src/pages/about/template.pug', filename: 'public/[name].html' },
examples: { import: './vendor/examples/index.html', filename: 'public/some/path/[name].html' },
},
};
webpack entry
API for the plugin option filename
, its can be as a template string
or a function
const PugPluginOptions = {
filename: (pathData, assetInfo) => {
return pathData.chunk.name === 'main' ? 'assets/css/styles.css' : '[path][name].css';
}
}
const PugPluginOptions = {
modules: [
{
test: /\.(pug)$/,
sourcePath: path.join(__dirname, 'src/templates/'),
outputPath: path.join(__dirname, 'public/'),
filename: '[name].html'
},
{
test: /\.(html)$/,
sourcePath: path.join(__dirname, 'src/vendor/static/'),
outputPath: path.join(__dirname, 'public/some/other/path/'),
},
{
test: /\.(sass|scss)$/,
sourcePath: path.join(__dirname, 'src/assets/sass/'),
outputPath: path.join(__dirname, 'public/assets/css/'),
filename: isProduction ? '[name].[contenthash:8].css' : '[name].css'
},
],
};
post process
for modules to handle the extracted content before emit
const PugPluginOptions = {
modules: [
{
test: /\.pug$/,
postprocess: (content, info, compilation) => {
// TODO: your can here handle extracted HTML
return content;
},
},
],
};
pug
files
const PugPlugin = require('pug-plugin');
module.exports = {
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
},
],
},
};
See the description of the
pug-loader
options here.
?prop=PROPERTY_NAME
//- srcset from property `srcSet` of result of responsive-loader
img(srcset=require('./image.jpg?prop=srcSet') alt="responsive image")
//- image with fixed size
img(src=require('../img/image.jpg?size=320') alt="image 320px")
The plugin options are default options for self plugin and all plugin modules
.
In a defined module
any option can be overridden.
enabled
Type: boolean
Default: true
Enable/disable the plugin.
test
Type: RegExp
Default: /\.pug$/
The search for a match of entry files.
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 parameters are available in the function:
@param {webpack PathData} pathData
See the description of this type here@param {webpack AssetInfo} assetInfo
@return {string}
The name or template string of output file.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} outputFile The absolute path to generated output file (issuer of asset).
* @property {string | (function(PathData, AssetInfo): string)} filename The filename template or function.
* @property {string} sourceFile The absolute path to source file.
* @property {string} assetFile The output asset file relative by `output.publicPath`.
*/
modules
Type: PluginOptions[]
Default: []
The array of objects of type PluginOptions
to separately handles of files of different types.
The description of @property
of the type PluginOptions
see above, by Plugin options.
/**
* @typedef {Object} PluginOptions
* @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
*/
verbose
Type: boolean
Default: false
Show the file information at processing of entry.
pug-plugin
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 for 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({
modules: [
PugPlugin.extractCss({
// output filename for CSS files
filename: 'assets/css/[name].[contenthash:8].css'
})
]
})
],
module: {
rules: [
{
test: /\.pug$/,
loader: PugPlugin.loader,
options: {
method: 'render',
}
},
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader']
},
{
test: /\.(png|jpg|jpeg|ico)/,
type: 'asset/resource',
generator: {
// output filename for images
filename: 'assets/img/[name].[hash:8][ext]',
},
},
{
test: /\.(woff|woff2|eot|ttf|otf|svg)$/i,
type: 'asset/resource',
generator: {
// output filename for 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(rel='stylesheet' href=require('./styles.scss'))
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.
@import '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
font-style: normal;
font-weight: 400;
}
body {
font-family: 'Montserrat', serif;
}
.header {
width: 100%;
height: 120px;
background-image: url('~Images/header.png'); // pug-plugin can resolve url
}
💡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";
font-style: normal;
font-weight: 400;
font-display: block;
src:
url(/assets/fonts/material-icons.woff2) format("woff2"), /* <-- */
url(/assets/fonts/material-icons.woff) format("woff"); /* <-- */
}
.material-icons {
font-family: "Material Icons";
font-weight: normal;
font-style: normal;
font-size: 24px;
/* ... */
}
/* ... */
/*
* Fonts from local directory.
*/
@font-face {
font-family: 'Montserrat';
src: url(/assets/fonts/Montserrat-Regular.woff2); /* <-- */
font-style: normal;
font-weight: 400;
}
body {
font-family: 'Montserrat', serif;
}
.header {
width: 100%;
height: 120px;
background-image: url(/assets/img/header.4fe56ae8.png); /* <-- */
}
💡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.
.html
file in separate fileDependency: html-loader
This loader is need to handle the .html
file type.
Install: npm install html-loader --save-dev
webpack.config.js
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'public/'),
publicPath: '/',
},
entry: {
'example': './vendor/pages/example.html', // output to /static/example.html
},
plugins: [
new PugPlugin({
modules: [
// add the module to match `.html` files in webpack entry
{
test: /\.html$/,
filename: '[name].html', // output filename
outputPath: 'static/', // output path for all .html files defined in entry
},
],
}),
],
module: {
rules: [
// add the loader to handle `.html` files
{
test: /\.html$/,
loader: 'html-loader',
options: {
sources: false, // disable processing of resources in static HTML, leave as is
esModule: false, // webpack use CommonJS module
},
},
],
},
};
Dependencies:
css-loader
handles .css
files and prepare CSS for any CSS extractorsass-loader
handles .scss
filessass
compiles Sass to CSSInstall: npm install css-loader sass sass-loader --save-dev
webpack.config.js
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'public/'),
publicPath: '/',
},
entry: {
'css/styles': './src/assets/main.scss', // output to public/css/styles.css
},
plugins: [
new PugPlugin({
modules: [
// add the module to extract CSS
// see options https://github.com/webdiscus/pug-plugin#options
PugPlugin.extractCss({
filename: '[name].[contenthash:8].css',
})
],
}),
],
module: {
rules: [
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader'],
},
],
},
};
When using
PugPlugin.extractCss()
to extract CSS fromwebpack entry
the following plugins are not needed:
- mini-css-extract-plugin
- webpack-remove-empty-scripts - fix plugin for mini-css-extract-plugin
The plugin module
PugPlugin.extractCss
extract and save pure CSS without empty JS files.⚠️ When using
PugPlugin.extractCss()
don't use thestyle-loader
.
⚠️ Limitation for CSS
The@import
CSS rule is not supported. This is a BAD practice, avoid CSS imports. Use any CSS preprocessor like the Sass to create a style bundle using the preprocessor import.
pug-plugin
and pug-loader
with html
render method.Don't use it if you don't know why you need it.
It's only the example of the solution for possible trouble by usage thehtml-loader
.
Usually is used therender
orcompile
method inpug-loader
options.
For example, by usage in pug both static and dynamic resources.
index.pug
html
head
//- Static resource URL from public web path should not be parsed, leave as is.
link(rel='stylesheet' href='/absolute/assets/about.css')
//- Required resource must be processed.
Output to /assets/css/styles.8c1234fc.css
link(rel='stylesheet' href=require('./styles.scss'))
body
h1 Hello World!
//- Static resource URL from public web path should not be parsed, leave as is.
img(src='relative/assets/logo.jpeg')
//- Required resource must be processed.
Output to /assets/images/image.f472de4f4.jpg
img(src=require('./image.jpeg'))
webpack.config.js
const fs = require('fs');
const path = require('path');
const PugPlugin = require('pug-plugin');
module.exports = {
mode: 'production',
output: {
path: path.join(__dirname, 'public/'),
publicPath: '/',
},
entry: {
index: './src/index.pug',
},
plugins: [
new PugPlugin({
modules: [
PugPlugin.extractCss({
filename: 'assets/css/[name].[contenthash:8].css',
})
],
}),
],
module: {
rules: [
{
test: /\.pug$/,
use: [
{
loader: 'html-loader',
options: {
// Webpack use CommonJS module
esModule: false,
sources: {
// ignore not exists sources
urlFilter: (attribute, value) => path.isAbsolute(value) && fs.existsSync(value),
},
},
},
{
loader: PugPlugin.loader,
options: {
method: 'html', // usually is used the `render` method
},
},
],
},
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader'],
},
{
test: /\.(png|jpg|jpeg)/,
type: 'asset/resource', // process required images in pug
generator: {
filename: 'assets/images/[name].[hash:8][ext]',
},
},
],
},
};
⚠️ When used
PugPlugin
andhtml-loader
A static resource URL from a public web path should not be parsed by the
html-loader
. Leave the URL as is:img(src='/assets/image.jpg') link(rel='stylesheet' href='assets/styles.css')
Loading a resource with
require()
should be handled via webpack:img(src=require('./image.jpg')) link(rel='stylesheet' href=require('./styles.css'))
For this case add to
html-loader
the option:
sources: { urlFilter: (attribute, value) => path.isAbsolute(value) && fs.existsSync(value) }
To enable live reload by changes any file add in the webpack config following options:
devServer: {
static: {
directory: path.join(__dirname, './dist'),
watch: true, // <--
},
devMiddleware: {
writeToDisk: true, // <--
},
port: 9000,
},
npm run test
will run the unit and integration tests.
npm run test:coverage
will run the tests with coverage.
2.1.1 (2022-04-20)
responsive-loader
FAQs
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.