Replace CSS url with Gulp task - html

I try to replace the path of a CSS file in index.html using Gulp. The problem is I have multiple projects with different theme name, and the path of the source file is different in the distribution package
index.html
<link href="app/style/themes/dark/theme.css" id="hmi-theme" rel="stylesheet" />
In my distribution package, the theme is copied under something like src\projects\project\app\style\themes
dist/index.html
<link href="[set the path here/]app/style/themes/dark/theme.css" id="hmi-theme" rel="stylesheet" />
Here is an attempt with gulp-find to find the url in index.html:
var gulp = require('gulp');
var find = require('gulp-find');
var replace = require('gulp-replace');
gulp.task('templates', function(){
gulp.src(['index.html'])
.pipe(find(/app\/style\/themes\/([^"]*)/g))
.pipe(gulp.dest('file.txt'));
That works, I've got the value in the destination file. But is it possible to use this value with gulp-replace to change the value in the HTML file?
I've tried also something like:
.pipe(replace(/app\/style\/themes\/([^"]*)/g, "dist/" + find(/app\/style\/themes\/([^"]*)/g)))
But the value in index.html was:
dist/[object Object]

Ok, I've finally found a solution:
return gulp.src(path.join(projectDir, 'index.html'))
.pipe(replace(/app\/style\/themes\/([^"]*)/g, function(cssPath) {
return "my-new-path/" + cssPath;
} ))
.pipe(gulp.dest(distDir));

Related

Combining Relative Files in Gulp

I need to combine some HTML files with the CSS that resides in the same directory using Gulp. The file structure of my project is as follows.
- src
- dir1
- index.html
- style.css
- dir2
- index.html
- style.css
- dir3
- index.html
- style.css
So, I'm combining the HTML and CSS from dir1, then the HTML and CSS from dir2, and so on.
I've tried to do this several ways (including the following) but can't get anything to work the way I want.
.pipe(replace('<link rel="stylesheet" href="style.css">', function (match, p1) {
return '<style>' + fs.readFileSync('src/' + p1, 'utf8') + '</style>';
}))
Is there an easy way to reference relative files in Gulp?
I assume you are using gulp 4 and gulp-replace, and that your gulpfile.js is located in the project directory, next to the src subdirectory.
Then the task consists of three steps:
read the index.html files
replace the string <link rel="stylesheet" href="style.css"> with a <style> tag with the contents of the file style.css in the same directory as index.html.
write the changed index.html files to a new destination directory.
Steps 1 and 3 are easy to accomplish with gulp.src and gulp.dist, so let's look at step 2. Each style.css (the file we want to read) resides in the same directory as index.html. That directory can be retrieved with this.file.dirname in the gulp-replace callback. If we append "style.css" to that directory, we will get a full path to a CSS file that can be read with readFileSync. The rest is pretty straightforward:
const { readFileSync } = require('fs');
const gulp = require('gulp');
const replace = require('gulp-replace');
const { join } = require('path');
exports.default = () =>
gulp.src('src/**/index.html')
.pipe(replace('<link rel="stylesheet" href="style.css">', function () {
const stylePath = join(this.file.dirname, 'style.css');
const style = readFileSync(stylePath);
return `<style>${style}</style>`;
}))
.pipe(gulp.dest('dest'));
I really think that the only unobvious part in this process is getting the directory of each index.html/style.css file pair in the gulp-replace callback as this.file.dirname. Here, according to gulp-replace:
The value of this.file will be equal to the vinyl instance for the file being processed.
and file.dirname for a vinyl file
Gets and sets the dirname of file.path. Will always be normalized and have trailing separators removed.

Gulp task to merge JSON and Handlebars from partials into HTML

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.

How to dynamically specify destination folder to gulp

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.

Gulp-Rev-replace not replacing my the file path with the gulp-rev hash

Similar to this question here: How do I replace the filenames listed in index.html with the output of gulp-rev?
Im using gulp-useref to combine the files listed in my build blocks. I would like to use gulp-rev to add a hash to the end of the concatenated file and update my markup with the new filename.
As per the gulp-rev-replace doc, im using this almost exactly with minor tweaks:
var gulp = require('gulp');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var useref = require('gulp-useref');
var filter = require('gulp-filter');
var uglify = require('gulp-uglify');
var csso = require('gulp-csso');
var RevAll = require('gulp-rev-all');
gulp.task("index", function () {
var jsFilter = filter("js/**/*.js");
var cssFilter = filter("css/**/*.css");
var revAll = new RevAll();
var userefAssets = useref.assets();
return gulp.src("Views/**/*.cshtml")
.pipe(userefAssets) // Concatenate with gulp-useref
.pipe(jsFilter)
.pipe(uglify()) // Minify any javascript sources
.pipe(jsFilter.restore())
.pipe(cssFilter)
.pipe(csso()) // Minify any CSS sources
.pipe(cssFilter.restore())
.pipe(rev()) // Rename the concatenated files
.pipe(userefAssets.restore())
.pipe(useref())
.pipe(revReplace()) // Substitute in new filenames
.pipe(gulp.dest('public'));
});
my html looks like so:
<!-- build:css css/combined.css -->
<link href="css/components/style1.css" rel="stylesheet">
<link href="css/components/style2.css" rel="stylesheet">
<link href="css/pages/style3.css" rel="stylesheet">
<!-- endbuild -->
<!-- build:js js/lib.js -->
<script type="text/javascript" src="js/global/js1.js"></script>
<script type="text/javascript" src="js/vendor/moment.js"></script>
<script type="text/javascript" src="js/components/component.calendar.js"></script>
<script type="text/javascript" src="js/pages/jaunt-hotels/matrix.js"></script>
<!-- endbuild -->
the output I get is:
<link rel="stylesheet" href="css/combined.css">
<script src="js/lib.js">
the </script>
but what im looking for is something like:
<link rel="stylesheet" href="css/combined-ABC234.css">
<script src="js/lib-TYU765.js"></script>
directory structure:
root
|-css
|-style1.css
|-style2.css
|-style3.css
|-js
|-*js files here
|-views
|-*Many folders with .cshtml files
|-gulp
|-tasks
|-bundle-assets.js <- task that contains code above
Any help is greatly appreciated!
You need to pass an option to revReplace(), like so:
.pipe(revReplace({replaceInExtensions: ['.cshtml']}));
By default, gulp-rev-replace will perform changes only on ['.js', '.css', '.html', '.hbs'].
More information on https://www.npmjs.com/package/gulp-rev-replace#options-replaceinextensions.
I was working with .ejs and had a similar issue. Hope this helps..
Thanks for updating your question! I tried your Gulp-Setup yesterday and found out, that the useref and rev plugin's won't work if the path to your assets are incorrect. In your sample, the *.cshtml files are all in the folder views, but pointing to ressources which are on one level above. Going from a file inside views (like views/index.cshtml, the path css/components/style1.css cannot be resolved, because it should actually be ../css/components/style1.css. Therefore useref gets an empty resultset, does not create any files, and rev cannot update because it has no point to reference to.
Get the paths in your cshtml right so that they're relative to the real ressources, than it will work. I hope you can because that setup looks a little Dotnetty, and I'm not sure if you have freedom there to change things ;-)

Apply conditions in gulp.dest

I have a gulp task which gets the html file, finds the scripts inside it, minifies them, applies revisions and outputs everything in the assets folder:
templates/index.html
<!-- build:js js/app.js -->
<script src="lib/js/a.js"></script>
<script src="lib/js/b.js"></script>
<!--endbuild -->
gulpfile.js
var useref = require('gulp-useref'),
filter = require('gulp-filter'),
uglify = require('gulp-uglify'),
rev = require('gulp-rev'),
revReplace = require('gulp-rev-replace');
gulp.task('build',function() {
var assets = useref.assets({searchPath: './'}),
jsapp = filter('**/app.js'),
return gulp
.src(gulp.src('templates/index_src.html'))
// collect all assets from source file by the means of useref
.pipe(assets)
//build js/app.js
.pipe(jsapp)
.pipe(uglify())
.pipe(jsapp.restore())
// Take inventory of the file names for future rev numbers
.pipe(rev())
// Apply the concat and file replacement with useref
.pipe(assets.restore())
.pipe(useref())
// Replace the file names in the html with rev numbers
.pipe(revReplace())
// output files
.pipe(gulp.dest('./'));
});
This works fine but outputs everything (js/app.js and index.html) to the root directory (./);
Is there are any way to apply a condition inside the gulp.dest and output js/app.js to the root folder of the project but output index.html to a different location (e.g. ./templates_cache)?
Also add a filter for your html files, to be able to output only those files to a different folder:
var htmlFilter = filter('**/*.html')
Change your output to:
// output for js files
.pipe(jsapp)
.pipe(gulp.dest('./'))
.pipe(jsapp.restore())
// output for html files
.pipe(htmlFilter)
.pipe(gulp.dest('./templates_cache'));