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'));
Related
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.
Project structure:
📁 development
📁 public
📁 pug
📁 1sass
📁 2css
📁 admin
📁 pug
📁 3sass
📁 4css
I add digits to folder names to imitate the situations when gulp can not guess somehow which output folder is respects to input ones.
Now, I want to compile .sass files in public/1sass and admin/3sass to .css and put it in public/2css and admin/4css respectively:
📁 public/1sass → 📁 public/2css
📁 admin/3sass → 📁 admin/4css
How I need to setup the sass task in gulpfile? Even if we put the paths array to gulp.src, how gulp will understand which output path respects to input ones?
Maybe gulp.parallel() becomes available in gulp 4.x will do?
Update
Two things that I did not understand yet:
How I should to setup the multiple output paths in gulp.dest()?
I learned that file.dirname = path.dirname(file.dirname); removes the last parent directory of the relative file path.But how I should to setup it for each of 1sass ans 3sass? Via array?
const gulp = require('gulp'),
sass = require('gulp-sass'),
path = require('path'),
rename = require('gulp-rename');
gulp.task('sass', function(){
return gulp.src([
`development/public/1sass/*.sass`,
`development/public/3sass/*.sass`])
.pipe(sass())
// As I can suppose, here we must to setup output paths for each input one
.pipe(rename(function(file){
file.dirname = path.dirname(file.dirname);
}))
.pipe(/* ??? */);
});
Simply in case of dynamic src and you want respective same dest (as received in src) then you can use following
Example Suppose we have array of scss file:
var gulp = require('gulp');
var sass = require('gulp-sass');
var scssArr = [
'src/asdf/test2.scss',
'src/qwerty/test1.scss'
];
function runSASS(cb) {
scssArr.forEach(function(p){
gulp.src(p, {base:'.'})
.pipe(sass({outputStyle: 'compressed'}))//outputStyle is optional or simply sass()
.pipe(gulp.dest('.')); //if othe folder including src path then use '/folder-name' instead of '.', so output path '/folder-name/{src-received-path}'
})
cb();
}
exports.runSASS = runSASS; // gulp runSASS
Run command gulp runSASS This will create following files:
src/asdf/test2.css
src/qwerty/test1.css
Happy Coding..
See my answer to a similar question: Gulp.dest for compiled sass. You should be able to modify that easily for your purposes. If you have trouble edit your question with your code and you will get help.
Even if we put the paths array to gulp.src, how gulp will understand which output path respects to input ones?
Gulp will retain the relative paths for each file that it processes. So, in your case, the files in public/1sass will all have their relative path info after sass processing still intact. And the files in admin/3sass will all have their relative path info as well. Thus you only need to find a way to modify that path info (parent directory structure) to redirect the files to a desired destination.
In your case, that would involve removing the immediate parent directory and replacing it with the 'css' directory. Gulp-rename is one way, not the only way, to do that. In gulp-rename you can examine and modify the parent directory structure - it is just string manipulation.
Maybe gulp.parallel() becomes available in gulp 4.x will do?
No, gulp.parallel() will not be of any help here. It will just order the execution and finishing of different tasks. It would not be necessary or of any real help in your case.
[EDIT]
var gulp = require("gulp");
var rename = require("gulp-rename");
var path = require("path");
var sass = require("gulp-sass");
gulp.task('modules-sass', function () {
// using .scss extensions for sass files
return gulp.src(`development/**/*.scss`)
.pipe(sass())
.pipe(rename(function (file) {
// file.dirname before any changes
console.log("file.dirname 1 = " + file.dirname);
// this removes the last directory
var temp = path.dirname(file.dirname);
console.log(" temp = " + temp);
// now add 'Css' to the end of the directory path
file.dirname = path.join(temp, 'Css');
console.log(" after = " + file.dirname);
}))
.pipe(gulp.dest('development'));
});
// this is the directory structure I assumed
// gulpfile.js is just above the 'development' directory
// development / Admin / Sass1 / file1.scss
// development / Admin / Sass1 / file2.scss
// development / Admin / Sass2 / file3.scss
// development / Admin / Sass2 / file4.scss
// development / Admin / Css
// development / Public / Sass1 / file5.scss
// development / Public / Sass1 / file6.scss
// development / Public / Sass2 / file7.scss
// development / Public / Sass1 / file8.scss
// development / Public / Css
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 using gulp-watch to watch for changes
and right now I have it ignore layout files. The problem is that whenever I update a layout file, I have to change some other file for it to compile. Is there any way using gulp-watch to watch everything and then compile a part of it? I saw this relevant link but it did not use gulp-watch.
I misread this question. I've left my original answer at the bottom for reference anyway.
You could use gulp-if.
gulp.task('stream', function () {
return gulp.src('dir/**/*.*')
.pipe(watch('dir/**/*.*'))
.pipe(gulpif(function (file) {
return file.ext != ".layout"//your excluded extension
}, processIfTrue()))
.pipe(gulp.dest('build'));
});
That link does use gulp-watch. In fact, as I understand, that link explains exactly what you want to do.
The gulp-watch and whatever task you run on change take separate gulp.src instances.
You can, for example, use gulp.src('**/*.*') for your gulp.watch, and then gulp.src('**/*.less') for your compilation task.
You can set 2 separate watchers to run, and modifying each respective file listed below in src would trigger the respective task for that filename:
$ tree -I node_modules
.
├── gulpfile.js
├── package.json
└── src
├── layout-file-1.html
├── layout-file-2.html
├── other-file-1.html
└── other-file-2.html
1 directory, 6 files
gulpfile.js - gulp.watch() function
var gulp = require('gulp')
// files with the word "layout" in them
var layoutFiles = 'src/**/*layout*';
// files without the word "layout" in them
var otherFiles = ['src/**/*', '!'+layoutFiles];
// these tasks will show as completed in console output
gulp.task('build-layout-files');
gulp.task('build-other-files');
gulp.task('watch', function(cb) {
// watch only layoutFiles
gulp.watch(layoutFiles, ['build-layout-files'])
// watch only otherFiles
gulp.watch(otherFiles, ['build-other-files'])
})
gulp.task('default', ['watch'])
gulpfile.js - gulp-watch module
var gulp = require('gulp')
var watch = require('gulp-watch')
// use print to debug watch processes
var print = require('gulp-print')
// files with the word "layout" in them
var layoutFiles = 'src/**/*layout*';
// files without the word "layout" in them
var otherFiles = ['src/**/*', '!'+layoutFiles];
gulp.task('watch:layout-files', function(cb) {
watch(layoutFiles, function () {
gulp.src(layoutFiles)
.pipe(print(function(fileName) {
return "Compiling Layout File: "+fileName;
}))
.pipe(gulp.dest('build/layout-files'))
});
})
gulp.task('watch:other-files', function(cb) {
watch(otherFiles, function () {
gulp.src(otherFiles)
.pipe(print(function(fileName) {
return "Compiling Other File: "+fileName;
}))
.pipe(gulp.dest('build/other-files'))
});
})
gulp.task('default', ['watch:layout-files', 'watch:other-files'])
I override the main directories for the Bootstrap in bower.json:
"main" : [
"./dist/css/bootstrap.css",
"./dist/css/bootstrap.css.map",
"./dist/css/bootstrap-theme.css",
"./dist/css/bootstrap-theme.css.map",
"./dist/js/bootstrap.js",
"./dist/fonts/*",
"./less/**"
]
And I want that a files were copied with css, js, fonts folders. I.e. can I set '/dist/' as a base forder?
Or can I do it in the gulp task? In gulpfile.js I wrote:
var files = mainBowerFiles('**/dist/**');
return gulp.src( files, {base: 'C:/Users/Den/Desktop/HTML5-Demo/bower_components/bootstrap/dist/'} )
.pipe( gulp.dest('public_html/libs') );
But I'm forced to write a full path which of course is bad. Is there way to use a relative path?
Also I want to ask what does '.' in the beginning of the directories mean?
To use relative path you need to get current working directory.
var path = require('path');
var cwd = process.cwd(); // current working directory
var basePath = path.resolve(cwd, "bower_components/bootstrap/dist");
The next code works:
var stream = gulp.src(files, {base: './bower_components/bootstrap/dist'})