I'm making a simple proof-of-concept website using JSON as a data source and Handlebars for a templating engine, with a view to 'merging' the two things into a static HTML file.
The website will be very modular, so each component will be built using a distinct Handlebars 'partial', which will each consume data from its own JSON file.
The development structure I have so far is like this:
src/
models/
header.json
article.json
footer.json
partials/
header.hbs
article.hbs
footer.hbs
index.hbs
The contents of the index.hbs file is something like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test App</title>
</head>
<body>
{{> header}}
{{> article}}
{{> footer}}
</body>
</html>
The partials are very simple for now. e.g header.hbs contains:
<header>{{header}}</header>
I have the following gulp file gulpfile.js which goes part way to what I want to achieve:
var gulp = require('gulp');
var data = require('gulp-data');
var handlebars = require('gulp-compile-handlebars');
var rename = require('gulp-rename');
gulp.task('default', function () {
var templateData = {
header: 'Welcome'
},
options = {
batch : ['./src/partials']
}
return gulp.src('src/index.hbs')
.pipe(handlebars(templateData, options))
.pipe(rename('index.html'))
.pipe(gulp.dest('dist'));
});
So far, it bundles in my partials together and outputs everything nicely, as HTML, in a file called dist/index.html.
The thing that's missing is the JSON data part. Currently the JSON data being consumed by the partials is defined in the gulp file (the variable templateData) but I want each partial to consume data from the src/models JSON files to provide clear separation. The name of the JSON file will be identical to the name of the .hbs partial it's consumed by.
I'm unclear how to go about this. The gulp-compile-handlebars documentation suggests that using gulp-data will support what I need, but I'm struggling to piece together anything from the gulp-data documentation that works for my specific use-case.
Could anyone please suggest how I could modify my gulp file to accomplish this?
Many thanks.
If I understand the question, you can use gulp-data to return an object from your models .json file and it will be added to your tempateData object.
Use gulp-data to pass a data object to the template based on the
handlebars file being processed. If you pass in template data this
will be extended with the object from gulp-data.
So this worked for me.
gulp.task('default', function () {
var templateData = {
header: 'Welcome'
},
options = {
batch : ['./src/partials']
}
return gulp.src('src/index.hbs')
.pipe(data(function(file) {
return require('./src/models/test.json');
}))
.pipe(handlebars(templateData, options))
.pipe(rename('index.html'))
.pipe(gulp.dest('dist'));
});
And you can modify the path to your .json file to match the name of the src .hbs file.
Related
I am using Webpack to compile my scripts and HTML (using html-webpack-plugin). The thing is, I have 5 HTML files that contains the same parts and I want to move these parts to separate .html files and then include these parts in every HTML file. This way, if I will change these smaller HTML files, it will recompile every HTML file to represent these changes.
Webpack does this for .js files by default, but can I use something like that for HTML files?
You can use <%= require('html!./partial.html') %> in your template. Example here: https://github.com/jantimon/html-webpack-plugin/blob/master/examples/custom-template/template.html
Another slightly different solution.
Using html-loader with interpolate option.
https://github.com/webpack-contrib/html-loader#interpolation
{ test: /\.(html)$/,
include: path.join(__dirname, 'src/views'),
use: {
loader: 'html-loader',
options: {
interpolate: true
}
}
}
And then in html pages you can import partials html and javascript variables.
<!-- Importing top <head> section -->
${require('./partials/top.html')}
<title>Home</title>
</head>
<body>
<!-- Importing navbar -->
${require('./partials/nav.html')}
<!-- Importing variable from javascript file -->
<h1>${require('../js/html-variables.js').hello}</h1>
<!-- Importing footer -->
${require('./partials/footer.html')}
</body>
html-variables.js looks like this:
const hello = 'Hello!';
const bye = 'Bye!';
export {hello, bye}
The only downside is that you can't import other variables from HtmlWebpackPlugin like this <%= htmlWebpackPlugin.options.title %> (at least I can't find a way to import them) but for me it's not an issue, just write the title in your html or use a separate javascript file for handle variables.
Check out Partials for HTML Webpack Plugin for something a little more elegant. It lets you set up HTML files and include them similar to what you're looking for simply like:
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackPartialsPlugin({
path: './path/to/partials/body.html'
})
]
Also configurable for where it gets added such as head vs body, opening vs closing, and lets you pass in variables.
You can use html-loader and posthtml. Then you can use the posthtml-include plugin.
const postHtml = require('posthtml');
const postHtmlInclude = require('posthtml-include');
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
esModule: false,
preprocessor: async (content, loaderContext) => {
try {
return await postHtml(postHtmlInclude()).process(content)).html;
} catch (error) {
loaderContext.emitError(error);
return content;
}
},
},
},
},
How can you use Gulp to gather in one html file a list of all the pages that are in the directory?
For example, in the build directory I have two files contact.html with title "Contacts" and faq.html with the title "Frequently asked questions", I need to get them and create a ui.html which would be a list of links to files of the form:
Frequently asked questions
Contacts
Well, with the addition of step your design (a connected css file).
Found the gulp-listing module, but it can not be customized, there it is as follows:
gulp.task('scripts', function() {
return gulp.src('./src/*.html')
.pipe(listing('listing.html'))
.pipe(gulp.dest('./src/'));
});
I used two gulp modules for do this.
gulp-filelist - for create file list
gulp-modify-file - for update this file
gulp
.src(['./html/**/*.html'])
.pipe(require('gulp-filelist')('filelist.js', { relative: true }))
.pipe(require('gulp-modify-file')((content) => {
const start = 'var list = '
return `${start}${content}`
}))
.pipe(gulp.dest('js'))
After run gulp, you got in js/filelist.js something like this:
var list = [
"Cancellation/template.html",
"Cancellation/email.html",
]
You can add this script in your html file, and with js display all info.
I have a folder structure where I keep all my assets similar to this.
-page1
-page1.html
-stylesheets
-page1
-page1style.css
-page2
page2.html
stylesheets
page2
page1style.css
I realize that this isn't the best folder structure but I choose it this way before I could have predicted problems. In my .html files I reference a stylesheet like so /stylesheets/name-of-page/foo.css. Now I am having problems writing a gulp script since all the minified files are being placed at the specified destination folder but have the following structure.
-build
-page1
-stylesheets
-page1.css
when I would like to have something like this
-build
-page1
-page.css
TL;DR or if my question is logic is scrambled : I would like to see the src path of the file at runtime and then perform some string manipulation to calculate its destination.
What you're looking for is gulp-rename:
var gulp = require('gulp');
var rename = require('gulp-rename');
gulp.task('default', function() {
gulp.src('src/**/*')
.pipe(rename(function(file) {
if (file.extname === '.css') {
file.dirname = //change directory to something else
file.basename = //change file name (without extension) to something else
}
}));
});
I also suggest you look into the path module instead of direct string manipulation to adjust the paths of your files.
I'm trying to use these two gulp plugins together:
gulp-html-minifier
gulp-inject-stringified-html
Or put differently, I'm trying to inject the contents of files containing html fragments into my javascript files after they're minified.
When I'm trying to run a straight up gulp build I get this:
Error: ENOENT: no such file or directory, open 'C:\path\to\.temp\template.html'
Here's a repro of my situation. My folder structure:
/src/app.js
/src/template.html
/gulpfile.js
/package.json
My gulpfile.js:
var gulp = require('gulp');
var injectHtml = require('gulp-inject-stringified-html');
var htmlmin = require('gulp-html-minifier');
gulp.task('minify', [], function() {
gulp.src('src/*.html')
.pipe(htmlmin())
.pipe(gulp.dest('.temp'));
});
gulp.task('default', ['minify'], function() {
gulp.src('src/*.js')
.pipe(injectHtml())
.pipe(gulp.dest('.build'));
});
The template.html file:
<div>My Template</div>
The app.js file:
var html = { gulp_inject: "../.temp/template.html" };
Now, if I run minify manually first, things will work as expected. From this I speculate I'm not using Gulp correctly. I reckon I'd need to pipe the result of htmlmin into the injectHtml method. But I fail to see how.
How can I get these two plugins to play together nicely?
You are missing a return in the minify task. It should look like that:
gulp.task('minify', [], function() {
return gulp.src('src/*.html')
.pipe(htmlmin())
.pipe(gulp.dest('.temp'));
});
Without return, the default task doesn't have any way to know that minify finished, so it may start before the minified html file was created.
How to use gulp to organize the different js file in different html?
is the only way I can do is to define each page to gulp task ? Or gulp have a better way can detect file automatically?
This is my situation below.
I have two html 'index.html','content.html'
index.html need plugin_A.js
content.html need plugin_B.js
And my gulp file:
gulp.task('index_concat', function() {
return gulp.src('./app/js/plugin_A.js')
.pipe(concat('index.js'))
.pipe(gulp.dest('./build/js/'));
});
gulp.task('content_concat', function() {
return gulp.src('./app/js/plugin_B.js')
.pipe(concat('content.js'))
.pipe(gulp.dest('./build/js/'));
});
If I had 100 pages, the tasks were too big!!!
I think this is a stupid way to define each page, but I have no idea how to get better. Please give me some advice.
You could use some name convention for your plugins like pluginName_index.js and pluginName_content.js . So you be able to do something like that:
function yourFunction(pluginName,targetName){
return gulp.src('./app/js/'+pluginName)
.pipe(concat(targetName))
.pipe(gulp.dest('./build/js/'));
}
fs.readdirSync('.app/js/pluginFolder')
.filter(function(fileName) {
var fileNameParts = fileName.split('_');
yourFunction(fileName,fileNameParts[1]);
});