Gulp: replace variables in HTML file based on file name - gulp

Our project is using Gulp. Now I have a requirement: we have multiple page-level HTML files, say login.html and my.html. Inside these original HTML files there is a variable called {{PAGE_TITLE}}, which should be replaced (by Gulp) to be "Login to System" and "My Account" respectively. Here is my current script:
gulp.task('pages', ['clean:tmp'], function () {
var pageTitle = '';
return gulp.src('/my/source/html/**/*.html')
.pipe(tap(function (file, t) {
pageTitle = /index.html$/.test(file.path) ? 'Login to System' : 'My Account';
}))
.pipe(replace(/{{PAGE_TITLE}}/g, pageTitle))
.pipe(gulp.dest('/my/dest/'));
});
It turns out the variable pageTitle is never set before the replace. I have searched for documentation of gulp-tap for tons of times, but I still do not know how to make it work. Please help, thanks.
This post: Modify file in place (same dest) using Gulp.js and a globbing pattern tries to achieve the same affect, but he resulted in some other solution.

I figured out the solution as followed:
gulp.task('pages', ['clean:tmp'], function () {
function replaceDynamicVariables(file) {
var pageTitle = /login.html$/.test(file.path) ? 'Login to System' : 'My Account';
file.contents = new Buffer(String(file.contents)
.replace(/{{PAGE_TITLE}}/, pageTitle)
);
}
return gulp.src('/my/source/html/**/*.html')
.pipe(tap(replaceDynamicVariables))
.pipe(gulp.dest('/my/dest/'));
});

Related

Looking for a Gulp plugin that will skip markdown files based on particular header attributes

I have markdown files that look like this
---
name: Some Name
date: '2013-09-09'
isCool: true
---
really cool text
I want to have a gulp task that only lets markdown through that has a particular property, for example isCool = true.
So I would imagine something like this
gulp.src('source/content/*/*.md')
.pipe(mdPrune({
isCool: true
}))
.pipe(gulp.dest('build/content/cool'));
then only the markdown that had an isCool attribute in the header would end up the build/content/cool folder.
gulp-filter would work.
const filter = require('gulp-filter');
gulp.task('default', function () {
// return true if want the file in the stream
const myFilter = filter(function (file) {
let contents = file.contents.toString();
return contents.match('isCool: true');
});
return gulp.src(['./src/*.md'])
.pipe(myFilter)
.pipe(gulp.dest('md'));
});
That will allow the file through if isCool: true is anywhere in the file. If that is a problem, just work on the regex to restrict it to the line after the date entry for example.
[The filter could also be defined outside of any task if it might be reused elsewhere or you just prefer it that way.

Get current file name in Gulp Stream

I've read Get the current file name in gulp.src(), and it seems like it's approaching what I am attempting to do, but I need help.
Consider the following function in a gulpfile.js:
function inline() {
return gulp.src('dist/**/*.html')
.pipe($.if(PRODUCTION, inliner('dist/css/app.css')))
.pipe(gulp.dest('dist'));
}
And inliner(), to be thorough (also in the gulpfile):
function inliner(css) {
var css = fs.readFileSync(css).toString();
var mqCss = siphon(css);
var pipe = lazypipe()
.pipe($.inlineCss, {
applyStyleTags: false,
removeStyleTags: false,
removeLinkTags: false
})
.pipe($.replace, '<!-- <style> -->', `<style>${mqCss}</style>`);
return pipe();
}
These functions take an external CSS file and inline them into the respective HTML for email.
I really want to know how to do something like this:
function inline() {
return gulp.src('dist/**/*.html')
.pipe($.if(PRODUCTION, inliner('dist/css/' + file.name + '.css')))
.pipe(gulp.dest('dist'));
}
And you might ask yourself, "why?" Well, I don't have just one CSS file. If everything from app.css was to be inlined, there would be a lot more styles applied than were actually necessary.
So I want to inline:
email1.css ---- to -------> email1.html
email2.css ---- to -------> email2.html
email3.css ---- to -------> email3.html
And so on. Essentially, I want to get the name of the HTML file being processed at that moment in the Gulp Stream, save it as a variable, and then pass it into the inliner('dist/css/' + file.name + '.css') bit. I've exhausted every bit of Gulp Knowledge I have and have come up completely and utterly blank.
Basically what you need to do is send each .html file in your stream down its own little sub stream with its own inliner(). The gulp-foreach plugin let's you do just that.
Then it's just a matter of determining the simple name of your file from its absolute path. The node.js built-in path.parse() got you covered there.
Putting it all together:
var path = require('path');
function inline() {
return gulp.src('dist/**/*.html')
.pipe($.if(PRODUCTION, $.foreach(function(stream, file) {
var name = path.parse(file.path).name;
return stream.pipe(inliner('dist/css/' + name + '.css'));
})))
.pipe(gulp.dest('dist'));
}

gulp-changed has different behaviours

In my website build-process, I use gulp-changed to prevent doing all task if unnecessary. However, sometimes it has different behaviours even if declarations are similar.
Does anyone know what I am doing wrong?
case 1 : building htlm / php using partials.
works fine! the task process only the first time, if I run it twice in a row
gulp.task('pages', function () {
var toBuild = src + config.pages.src_pages; // many php/html files
var partials = src + config.pages.src_partials; // "to be included"
var DEST = target;
return gulp.src([toBuild, "!" + partials])
.pipe(changed(DEST))
.pipe(fileinclude({ prefix: '##', basepath: '#file' }))
.pipe(size())
.pipe(gulp.dest(DEST));
});
case 2 : building a SVG sprite from multiple svg files.
doesn't work! the task runs again if I run it twice in a row
gulp.task('vector', function () {
var DEST = target + config.assets.images.vector_dest;
var configsvg = { // ... config stuff // };
return gulp.src(src + config.assets.images.vector_src)
.pipe(changed(DEST))
.pipe(svgo())
.pipe(svgSprite(configsvg)).on('error', function (error) {
console.log(error);
})
.pipe(size())
.pipe(gulp.dest(DEST));
});
The solution is to use gulp-newer module in case of "many to one" file compilation (concatenation etc...)
Thank you to Lim H.
gulp-newer vs gulp-changed
May I also suggest gulp-newy in which you can manipulate the path and filename in your own function. Then, just use the function as the callback to the newy(). This gives you complete control of the files you would like to compare.
This will allow 1:1 or many to 1 compares.
newy(function(projectDir, srcFile, absSrcFile) {
// do whatever you want to here.
// construct your absolute path, change filename suffix, etc.
// then return /foo/bar/filename.suffix as the file to compare against
}

How to pass a parameter to gulp-watch invoked task

I am trying to pass a parameter to a task that is being invoked by gulp-watch. I need it because I am trying to build a modular framework.
So if a file changes in module 1, the other modules don't need to be rebuild.
And I want just one function to create the concatted & uglified files per module.
This is what I got so far:
//here I need the 'module' parameter
gulp.task('script', function(module) { ... }
gulp.task('watch', function() {
gulp.watch('files/in/module1/*.js', ['script']); //here I want to pass module1
gulp.watch('files/in/module2/*.js', ['script']); //here I want to pass module2
});
A lot of the documentation/examples seems to be outdated (gulp.run(), gulp.start()).
I hope someone can help me out here.
I had the very same issue, searched for a while, and the "cleanest" way I came up with, uses the .on() event handler of gulp.watch(), and the .env property of gulp-util:
var gulp = require('gulp');
$.util = require('gulp-util');
var modules = {
module1: {}, // awesome module1
module2: {} // awesome module2
};
gulp.task('script', function(){
var moduleName = $.util.env.module;
// Exit if the value is missing...
var module = modules[moduleName];
if (!module) {
$.util.log($.util.colors.red('Error'), "Wrong module value!");
return;
}
$.util.log("Executing task on module '" + moduleName + "'");
// Do your task on "module" here.
});
gulp.task('watch', function () {
gulp.watch(['files/in/module1/*.js'], ['script']).on('change', function () {
$.util.env.module = 'module1';
});
gulp.watch(['files/in/module2/*.js'], ['script']).on('change', function () {
$.util.env.module = 'module2';
});
});
gulp-util also comes in handy if you need to pass (global) parameters from the shell:
[emiliano#dev ~]# gulp script --module=module1 --minify
Hope this helps someone else out there!
Regards.
In that i will answer directly the question "How to pass a parameter to gulp-watch invoked task"
My way of doing, and one of the possibility i see, is to use a global variable to pass the value between the two blocks. you set it just before launching the task in the watcher. And in the task, just at the start you pass it to a local variable.
See this answer for more details: https://stackoverflow.com/a/49733123/7668448
In what you want to achieve, you can too use just one watcher over the directory that hold all modules. If so is the structure. Then when a change happen, you can recover the changed file path. From that you can deduce what module does belong to. By getting the Module folder. That way you will not need to add a new watcher for each new module. Which can be nice when there is multiple contributors to the project for example when working on open source. And you do it one time, and don't have to care about adding anything. Just like with the delegation principle, with DOM event handling when there is multiple elements. Even if the chosen structure, doesn't have all the modules in one directory. You can stay pass multiple globs to the one watcher.
gulp.watch(['glob1/**/*.js', 'glob2/**/*.js',...], function(evt) {/*.....*/});
And following the structure you have, you can work your way to deduce what module is.
For the watcher here how i suggest you do it:
watch('./your/allModulesFolder/**/*.js', function (evt) {
rebuildModulWatchEvt = evt; //here you update the global var
gulp.start('rebuildModul'); // you start the task
})
The evt here hold multiple info: cwd, base, state, _contents ...etc And what interest us is path. So evt.path will give you the path of the changed file.
In your task either you do that:
gulp.task('rebuildModul', function() {
let evt = rebuildModulWatchEvt; // at all start you pass it to a local var
let filePath = evt.path; // how you get the changed file path
// your code go here for the rest, following your structure, get the path for the module folder
});
or you use a function :
gulp.task('rebuildModul', function() {
rebuildModulTaskRun(rebuildModulWatchEvt);
});
function rebuilModulTaskRun(evt) {
let filePath = evt.path;
// your code go here for the rest, following your structure, get the path for the module folder
}

Get the current file name in gulp.src()

In my gulp.js file I'm streaming all HTML files from the examples folder into the build folder.
To create the gulp task is not difficult:
var gulp = require('gulp');
gulp.task('examples', function() {
return gulp.src('./examples/*.html')
.pipe(gulp.dest('./build'));
});
But I can't figure out how retrieve the file names found (and processed) in the task, or I can't find the right plugin.
I'm not sure how you want to use the file names, but one of these should help:
If you just want to see the names, you can use something like gulp-debug, which lists the details of the vinyl file. Insert this anywhere you want a list, like so:
var gulp = require('gulp'),
debug = require('gulp-debug');
gulp.task('examples', function() {
return gulp.src('./examples/*.html')
.pipe(debug())
.pipe(gulp.dest('./build'));
});
Another option is gulp-filelog, which I haven't used, but sounds similar (it might be a bit cleaner).
Another options is gulp-filesize, which outputs both the file and it's size.
If you want more control, you can use something like gulp-tap, which lets you provide your own function and look at the files in the pipe.
I found this plugin to be doing what I was expecting: gulp-using
Simple usage example: Search all files in project with .jsx extension
gulp.task('reactify', function(){
gulp.src(['../**/*.jsx'])
.pipe(using({}));
....
});
Output:
[gulp] Using gulpfile /app/build/gulpfile.js
[gulp] Starting 'reactify'...
[gulp] Finished 'reactify' after 2.92 ms
[gulp] Using file /app/staging/web/content/view/logon.jsx
[gulp] Using file /app/staging/web/content/view/components/rauth.jsx
Here is another simple way.
var es, log, logFile;
es = require('event-stream');
log = require('gulp-util').log;
logFile = function(es) {
return es.map(function(file, cb) {
log(file.path);
return cb(null, file);
});
};
gulp.task("do", function() {
return gulp.src('./examples/*.html')
.pipe(logFile(es))
.pipe(gulp.dest('./build'));
});
You can use the gulp-filenames module to get the array of paths.
You can even group them by namespaces:
var filenames = require("gulp-filenames");
gulp.src("./src/*.coffee")
.pipe(filenames("coffeescript"))
.pipe(gulp.dest("./dist"));
gulp.src("./src/*.js")
.pipe(filenames("javascript"))
.pipe(gulp.dest("./dist"));
filenames.get("coffeescript") // ["a.coffee","b.coffee"]
// Do Something With it
For my case gulp-ignore was perfect.
As option you may pass a function there:
function condition(file) {
// do whatever with file.path
// return boolean true if needed to exclude file
}
And the task would look like this:
var gulpIgnore = require('gulp-ignore');
gulp.task('task', function() {
gulp.src('./**/*.js')
.pipe(gulpIgnore.exclude(condition))
.pipe(gulp.dest('./dist/'));
});
If you want to use #OverZealous' answer (https://stackoverflow.com/a/21806974/1019307) in Typescript, you need to import instead of require:
import * as debug from 'gulp-debug';
...
return gulp.src('./examples/*.html')
.pipe(debug({title: 'example src:'}))
.pipe(gulp.dest('./build'));
(I also added a title).