I have the following gulp task which I want to change the filename and contents of a file replacing any matching strings with the replacement.
The matching strings in the file contents get changed, but the file's name does not. I thought it would as my code appears to match the examples on https://www.npmjs.com/package/gulp-replace
What am I doing wrong?
function renameFileContents() {
return gulp.src([
'**/*',
'!.github/**',
'!languages/**',
'!node_modules/**',
'!.babelrc',
'!.editconfig',
'!.gitignore',
'!.travis.yml',
'!CHANGELOG.md',
'!codesniffer.ruleset.xml',
'!composer.json',
'!composer.lock',
'!config.yml',
'!config-default.yml',
'!gulpfile.babel.js',
'!MIT-LICENSE.txt',
'!package-lock.json',
'!package.json',
'!phpunit.xml.dist',
'!README.md',
'!webpack.config.js'
])
.pipe($.replace('BigTest', 'Tester'))
.pipe($.replace('Bigtest', 'Tester'))
.pipe($.replace('bigtest', 'tester'))
.pipe(gulp.dest('./'));
}
Use gulp-rename to alter filenames. Add:
const rename = require('gulp-rename');
and before .pipe(gulp.dest('./'));:
.pipe(
rename(function(path) {
path.basename = path.basename.replace(/BigTest|Bigtest|bigtest/, function(matched) {
return { BigTest: 'Tester', Bigtest: 'Tester', bigtest: 'tester' }[matched];
});
})
)
You asked in a comment why new files are created (with the new names) but the original files still remain. Why does gulp-rename not actually rename the original files as you might expect?
Gulp-rename is not working with the original files. This can be a little confusing.
It's called gulp-rename because it renames an in-memory gulp file
object. gulp is like functional programming, each plugin takes in
input and produces output in-memory without causing side effects. [emphasis added]
gulp works like this:
read file (gulp.src)
do some stuff, modify the file in-memory (plugins)
commit file changes back to fs (gulp.dest/or others)
From gulp-rename issues: not renaming the original files.
The suggested fix (from gulp recipes: deleting files from a pipeline) which I tested is:
const del = require('del');
const vinylPaths = require('vinyl-paths');
and add this pipe before the replace pipe:
.pipe(vinylPaths(del))
.pipe(
rename(function(path) { ......
and your original files will be deleted, leaving only the newly named files. Obviously, make sure you test this on good test cases before deleting any of your files!
Related
I have created a glob for gulp which ignores javascript and coffeescript files within a set of directories. I'd like it to copy all other files into a directory which works fine. The only problem is that when there are only javascript or coffeescript files it copies an empty folder. Any ideas how this glob could be amended to not copy empty folders?
gulp.task('copyfiles', function(){
gulp.src('apps/*/static_src/**/!(*.js|*.coffee)')
.pipe(gulp.dest('dest'));
});
Example source files:
apps/appname/static_src/images/image.jpg
apps/appname/static_src/js/script.js
Expected output:
dest/static_src/images/image.jpg
Current output:
dest/static_src/images/image.jpg
dest/static_src/js/
Since gulp.src accepts almost the same options as node-glob, you can add nodir: trueas an option:
gulp.src('apps/*/static_src/**/!(*.js|*.coffee)', { nodir: true })
This will preserve the dir structure from src, but omit empty ones.
gulp.task('copyfiles', function(){
gulp.src(['apps/*/static_src/**/*','!apps/*/static_src/{js/, js/**}'])
.pipe(gulp.dest('dest'));
});
I think you need a pattern '!apps/*/static_src/{js/, js/**}' that matches the directory as well as the files inside to prevent ommiting an empty directory. I am not sure if there is a pattern to match a directory only by specifying its content.
I have a task:
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
When I am adding new empty ".scss" file to '/admin_app/scss/' and running task from above, empty ".scss" files is copied to destination folder. If file is not empty everything is ok: a valid css file( with ".css" extension) is compiled and no ".scss" files are copied. The problem is when I add new ".scss" file to "/admin_app/scss/" directory, a "watch" task is triggered, and because file is empty, it is copied to destination directory. As a result, a lot of unneeded garbage is dest folder. Why this happens and how can I get rid of it?
UPDATED
My "watch" and "default" tasks:
gulp.task('watch', ['compile_scss'], function() {
apps.forEach(function(appName) {
gulp.watch('/admin_app/scss/*.scss', ['compile_scss']);
});
});
gulp.task('default', ['watch']);
One way to solve this problem would be to simply filter the empty files.
Try something like this:
var filter = require('gulp-filter'),
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(filter(function(a){ return a.stat && a.stat.size }))
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
There's also a plugin specifically for this purpose. You can use it like this:
var clip = require('gulp-clip-empty-files'),
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(clip())
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
In addition: there seem to have been several reports of problems in gulp-sass and underlying libraries when compiling empty files. There is a Github issue for gulp-sass, reporting this should be solved in the 2.x versions of the plugin. If you're already running 2.x, the problem you are facing might be an issue introduced by solving the original problem.
If you add empty scss files in your sass folder, prefix them with underscore: _empty.scss.
See "Partials" here: http://sass-lang.com/guide#topic-4
You can create partial Sass files that contain little snippets of CSS
that you can include in other Sass files. This is a great way to
modularize your CSS and help keep things easier to maintain. A partial
is simply a Sass file named with a leading underscore. You might name
it something like _partial.scss. The underscore lets Sass know that
the file is only a partial file and that it should not be generated
into a CSS file. Sass partials are used with the #import directive.
I tried to create a gulpfile.js for my personal website project. I've never done this before but with a little 'trial and error' it now works in an acceptable way.
The only thing that doesn't work even after 1000 modifications is simple copying files and folders.
var files = {
data_src : [
'./files.json',
'data/**/*.*'
],
distribution_dest : '_distribution'
};
gulp.task('copy-data', function() {
gulp.src(files.data_src, { base: './' })
.pipe(gulp.dest(files.distribution_dest))
.pipe(notify({message: 'Data copied for distribution!'}));
});
This should copy all sub-folders and files to the gulp.dest. But it copies only half of them, some folders will be ignored even if I change their names etc. (no special characters, same subfolder structure as the once that got copied correctly ...) - nothing worked. I just can't see any pattern in this.
There is no error message while running gulp. Nothing that would help me find the error.
Why are some folders or files excluded from copying?
I use base to keep the folder / sub-folder structure; tried with and without 'base' -> no effects on the copying process.
I also changed the position of the 'copy-data' task in the run-list. Actually it's the first task to run. There seems to be no change in behavior no matter if it's the first or the last one.
gulp.task('default', function() {
gulp.run('copy-data', 'custom-sass', 'framework-sass', 'custom-js', 'framework-js', 'replace-tags', 'browser-sync');
... some watches ...
});
The structure of the data folder looks like these:
./data
|-doc
|---content
|---template
|-img
|---chart
|---icon
|---logo
|---pattern
|---people
|---photo
|---symbol
|-----brandklassen
|-----brandschutzzeichen
|-----gebotszeichen
|-----gefahrensymbole
|-----rettungszeichen
|-----verbotszeichen
|-----verkehrsrechtzeichen
|-----warnzeichen
|---wallpaper
/data/doc and all subfolders are ok.
/data/img/chart to /data/img/people are also ok.
Within /data/img/photo only 14 out of 21 images are copied.
/data/img/symbol with sub-folders and /data/img/wallpaper were ignored completely.
SOLVED IT MYSELF! The problem was caused by async operating tasks. Adding a return forced gulp to complete the copying process before continuing!
gulp.task('copy-data', function() {
return gulp.src(files.data_src, { base: './' })
.pipe(gulp.dest(files.distribution_dest))
.pipe(notify({message: 'Data copied for distribution!'}))
});
Now all images will be copied!
I am trying to merge css and scss files into a main.css file that goes in my build directory.
Its working, but not in the right order. The style attributes from the scss files need to be in the bottom of the main.css file so they overrule the rest.
my Gulp task looks like this:
//CSS
gulp.task('css', function () {
var cssTomincss = gulp.src(['dev/css/reset.css', 'dev/css/style.css','dev/css/typography.css', 'dev/css/sizes.css']);
var cssFromscss = gulp.src(['dev/css/*.scss'])
.pipe(sass());
return es.merge(cssTomincss, cssFromscss)
.pipe(concat('main.css'))
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'))
});
I am defining the sources first with variables. I am using the gulp-sass plugin to convert the scss file into normal css (.pipe(sass)) and later merging the two with the es.merge function and concatenating them into main.css.
The problem is that the style attributes van the .scss files end up somewhere in the top end of the main.css file. I need them to be at the bottom. So they need to be concatenated at the bottom.
Any clue on how to do this?
Try streamqueue.
var streamqueue = require('streamqueue');
gulp.task('css', function () {
return streamqueue({ objectMode: true },
gulp.src(['dev/css/reset.css', 'dev/css/style.css', 'dev/css/typography.css', 'dev/css/sizes.css']),
gulp.src(['dev/css/*.scss']).pipe(sass())
)
.pipe(concat('main.css'))
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'))
});
This cheatsheet will help you. PDF is here.
It seems that the plugin gulp-order fits perfectly well in your case.
It allows you to re-order the passed stream with your own glob pattern, for example based on your code :
return es.merge(cssTomincss, cssFromscss)
.pipe(order([
'dev/css/reset.css',
'dev/css/style.css',
'dev/css/typography.css',
'dev/css/sizes.css',
'dev/css/*.css',
]))
.pipe(concat('main.css'))
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'))
One drawback of this is that you have to re-declare your globs, but you can get around by assign your globs to a value and then concat them in you order pipe, much cleaner.
You may have to set the base option to . of gulp-order as stated in their Readme if the files were not ordered correctly.
One another way would be to use stream-series, basically the same as event-stream, but the order of your stream is preserved, and you don't have to rewrite your globs.
I tried gulp-order without success: the order somehow wasn't taken into account.
The solution which worked for me was using stream-series, mentioned by Aperçu.
return streamSeries(
cssTomincss,
cssFromscss)
.pipe(concat('main.css'))
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'));
I failed with all provided answers, they produced some silent errors. Finally merge2 worked for me (seems like there was gulp-merge and later the project was renamed into merge2). I'm not sure why there is a need in streamify plugin, e.g. streams created with Rollup may produce "stream-not-supported-errors" with gulp-concat, gulp-uglify or gulp-insert.
const mergeStreams = require('merge2');
const streamify = require('streamify');
...
gulp.task('build', () => {
const streams = sources.map(createJSFile);
return mergeStreams(...streams)
.pipe(streamify(concat('bundle.js')))
.pipe(streamify(uglify()))
.pipe(gulp.dest('./dist'));
});
I'm trying to create a custom init task for a personal template in Grunt.
This is the js which generate my new project after a grunt init:mytemplate
exports.description = 'Try Grunt';
exports.warnOn = '*';
exports.template = function(grunt, init, done) {
grunt.helper('prompt', {type: 'skin'}, [
grunt.helper('prompt_for', 'name', 'trygrunt'),
grunt.helper('prompt_for', 'title', 'Im Trying GruntJS'),
grunt.helper('prompt_for', 'author_name', 'Myself')
],
function(err, props) {
var files = init.filesToCopy(props);
init.copyAndProcess(files, props);
done();
});
};
Everything works fine: files and folder are correctly generated or renamed from the root folder of the custom template based on rename.json info.
The question is: how can i also dynamically rename folders and not only files?
i.e.
{
"libs/name.js": "libs/{%= name %}.js" //this works fine
"src/name": "src/{%= name %}" //this doesn't work
}
The init.filesToCopy method only looks at renames.json for specific file (not directory) matches when it first builds the files object. Your best bet is to programmatically modify the files object between init.filesToCopy and init.copyAndProcess.
This is possible by modifying the result of the init.filesToCopy object. However you need to modify the key rather than the value of each pair.
For example, I have a folder called lib/ that I wish to copy the contents into app/
var files = init.filesToCopy(props),
libFolder = 'app';
// Repath the files correctly
for (var file in files) {
if (file.indexOf('lib/') > -1) {
var path = files[file],
newFile = file.replace('lib/', libFolder + '/');
files[newFile] = path;
delete files[file];
}
}
init.copyAndProcess(files, props);
It's also worth noting that rename.json works on the lib/ value not the new folder.
One thing that I've done is to use the props.main value to extract the libFolder value (e.g. libFolder = props.main.split('/')[0])
Another way to accomplish this and get around the limitation of not being able to rename folders, is to 1) setup a grunt-contrib-copy task to copy the folders and files and apply whatever names you require to the new folders/files, and then 2) have a grunt-contrib-clean task clean out the old files/folders resulting in the same net effect as renaming the folders.