Pass data to loader
rohit-gohri opened this issue ยท 6 comments
Is your feature request related to a problem? Please describe.
I'm using html-webpack-plugin to generate multiple static pages, all of them using a single template. Currently I'm rendering them differently in a custom loader by passing an id in the query part of the template and using that to render pages with different data.
This seems like a very hacky approach and instead I would like to specify the data where I'm creating pages itself, i.e. when adding a new HtmlWebpackPlugin instance.
Describe the solution you'd like
I would like to be able to pass this data via templateParams or a new option and have it passed to the loader's options/context.
Describe alternatives you've considered
Passing an id in query part of template and doing the data fetching in a custom loader.
Additional context
This is basically like an SSG setup but very simple, and I'm wondering if this is a supported use case or I should look for other tools?
@rohit-gohri
you can try to use new modern html-bundler-webpack-plugin.
Here is described exactly your usage case: How to pass different data into loader by multipage configuration.
@webdiscus Thanks! I didn't know about that, that does simplify my setup by a lot. But I still don't see my usecase which is that I have 1 template file - src/views/pages/[id].template.html
and I want to generate 3 pages out of it from diff data - /pages/1.html
, /pages/2.html
, /pages/3.html
. Since my template is same the resourcePath
is the same.
Currently I'm passing the id in the query part of the resource and doing something like this (pseudo code):
const template = `src/views/pages/[id].template.html`;
module.exports = {
output: {
publicPath: '/',
clean: true,
path: './dist/templates',
},
plugins: [
new HtmlWebpackPlugin({
template: `${template}?id=1`,
filename: 'pages/1.html',
}),
new HtmlWebpackPlugin({
template: `${template}?id=2`,
filename: 'pages/2.html',
}),
new HtmlWebpackPlugin({
template: `${template}?id=3`,
filename: 'pages/3.html',
}),
],
module: {
rules: [
// templates
{
test: /\.html/,
loader: HtmlBundlerPlugin.loader, // HTML template loader
options: {
preprocessor: (content, { resourcePath, resource }) => {
const [, query] = resource.split('?');
const data = findData(resourcePath, query);
return render(content, data);
},
},
},
],
},
};
Does this seem right or is there a better way to do this?
Using the HtmlBundlerPlugin, here is correct config:
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
const template = `src/views/pages/[id].template.html`;
module.exports = {
output: {
publicPath: '/',
clean: true,
path: path.join(__dirname, './dist/templates'),
},
entry: {
// define templates here
'pages/1': `${template}?id=1`, // => dist/templates/pages/1.html
'pages/2': `${template}?id=2`, // => dist/templates/pages/2.html
'pages/3': `${template}?id=3`, // => dist/templates/pages/3.html
},
plugins: [
// use the HtmlBundlerPlugin to handle an HTML template as the entry point
new HtmlBundlerPlugin({
js: {
// output filename of extracted JS from source script loaded in HTML via `<script>` tag
filename: 'assets/js/[name].[contenthash:8].js',
},
css: {
// output filename of extracted CSS from source style loaded in HTML via `<link>` tag
filename: 'assets/css/[name].[contenthash:8].css',
},
}),
],
module: {
rules: [
{
test: /\.html/,
loader: HtmlBundlerPlugin.loader, // HTML template loader
options: {
preprocessor: (content, { resourcePath, resource }) => {
const [, query] = resource.split('?');
const data = findData(resourcePath, query);
return render(content, data);
},
},
},
],
},
};
to simplify the config above, I have added the new entry
option in new version 0.8.0
.
This option has the identical Webpack Entry API plus has an additional data
property to pass data to the preprocessor
for the concrete template .
In the new configuration, you do not need to use a query for data identification.
The original Webpack entry still defines the templates, but without the data
property, because it doesn't match the Webpack entry API.
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
const template = `src/views/pages/[id].template.html`;
module.exports = {
output: {
publicPath: '/',
clean: true,
path: path.join(__dirname, './dist/templates'),
},
// entry: {}, // leave entry empty or undefined
plugins: [
// use the HtmlBundlerPlugin to handle an HTML template as the entry point
new HtmlBundlerPlugin({
entry: {
// define templates with custom data here
// note: this entry has Webpack entry API, plus 'data' property
'pages/1': { // => dist/templates/pages/1.html
import: template,
data: {
title: 'Home',
}
},
'pages/2': { // => dist/templates/pages/2.html
import: template,
data: {
title: 'Contact',
}
},
'pages/3': { // => dist/templates/pages/3.html
import: template,
data: {
title: 'About',
}
},
// if there is no data you can use simple syntax
'index': 'src/views/pages/home.html', // => dist/index.html
},
// output filenames
js: {
// output filename of extracted JS from source script loaded in HTML via `<script>` tag
filename: 'assets/js/[name].[contenthash:8].js',
},
css: {
// output filename of extracted CSS from source style loaded in HTML via `<link>` tag
filename: 'assets/css/[name].[contenthash:8].css',
},
}),
],
module: {
rules: [
{
test: /\.html/,
loader: HtmlBundlerPlugin.loader, // HTML template loader
options: {
preprocessor: (content, { resourcePath, data }) => {
// each template gets its own data passed in the entry data
return render(content, data);
},
},
},
],
},
};
What do you think about this? Is it better for you?
This is awesome!! Thanks! Will try it out
I have optimized the internal processing of the data passed to the preprocessor.
In new v0.9.0
the 3rd argument data
of the preprocessor
has been moved to the 2nd argument as a property.
NEW syntax:
preprocessor: (content, { resourcePath, data }) => {}