I have the following directory structure in my repos:
src
some-project
some-file.html
some-file.yml
other-project
foo.html
foo.yml
bar.html
bar.yml
stylesheet.css
dist
some-project
some-file.html
some-file.yml
other-project
foo.html
foo.yml
bar.html
bar.yml
I have a gulp task that takes the styles in any stylesheet added by an html file in the /src folder and automatically inlines them in the html (these are emails). The yml are meta-information used when sending data.
Now I want to add mustache templating to my HTML. The data for the templates is going to be in the yml files. The issue I have is that the gulp-mustache plugin takes a stream for its template input and an object in parameter for its data.
gulp.task('build', () => {
gulp.src('src/**/*.html')
.pipe(mustache(data)) // I don't have the data!
.pipe(inlineCss({
removeStyleTags: false
}))
.pipe(prefix(BASE_URL, [ { match: "img[src]", attr: "src"} ]))
.pipe(gulp.dest('dist'))
});
I have another task that can compile YML to JSON, but I was trying to avoid creating temporary files since it defeats the whole point of having gulp's virtual file streams. Besides, it doesn't solve the problem that I have two tasks and two gulp pipes, I can't pass the content of one file to the mustache function. Is there something I'm missing?
It seems like a very simple task and I've searched for hours without any progress. Isn't there a way for gulp to read files per pair?
Ok so first thing I notice is that you're not returning anything in your Gulp task, which prevents Gulp from knowing when your task finished.
Since the task itself is asynchronous and Gulp supports tasks returning a Promise that will signal completion when it resolves, you could do something like that, using node-yaml package which returns a Promise too:
const yaml = require('node-yaml')
gulp.task('build', () => {
return yaml.read('my-data.yml').then(data =>
new Promise(resolve => {
gulp.src('src/**/*.html')
.pipe(mustache(data))
// ...
.pipe(gulp.dest('dist'))
.on('end', resolve)
});
);
});
Or simply using callbacks:
gulp.task('build', callback => {
return yaml.read('my-data.yml', data => {
gulp.src('src/**/*.html')
.pipe(mustache(data))
// ...
.pipe(gulp.dest('dist'))
.on('end', callback)
});
});
Related
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!
I'm trying to update my compile TypeScript task (I've also tried it as a separate task as well) to combine the resulting javascript into one file. Here's my code:
gulp.task('compile:ts', function () {
return gulp
.src(tscConfig.filesGlob) //filesGlob is "src/ts/**/*.ts"
.pipe(plumber({
errorHandler: function (err) {
console.error('>>> [tsc] Typescript compilation failed'.bold.green);
this.emit('end');
}}))
.pipe(sourcemaps.init())
.pipe(tsc(tscConfig.compilerOptions))
.pipe(sourcemaps.write('.'))
.pipe(concat('all-components.js'))
.pipe(gulp.dest('public/dist'));
});
I've tried different destination folders, but I never even see the new file created, let alone used. Every example I've found shows that as the correct way and I don't get any errors so I can't tell what's wrong.
I am minifying an index.html file with gulp (note: took over this project, build system has been done by former dev).
It all works fine, but the gulp task generates a HTML file which has a cryptic extension to it, like:
index-bd2c7f58f0.html
I understand this must have it's advantage, but I can't grasp what...:) Because the disadvantage now is:
The node server needs the presence of an index.html file to allow the '/' route to work.
Thus so far, I either have to copy the file on every build or create a link which needs to be updated on every build
What am I missing? Should I just instruct gulp to create a plain index.html file, or what are best practices here?
Also, which of the various plugin calls is actually responsible for attaching that extension to the file name?
EDIT: Seems to be the gulp-rev and revReplace calls
Here is the gulp task I am using:
gulp.task('html', ['styles', 'scripts'], function () {
var client = buildHTML('./client/index.html', './dist/public');
return merge(client);
});
function buildHTML(index, distFolder) {
var lazypipe = require('lazypipe');
var saveHTML = lazypipe()
.pipe($.htmlmin, {
removeComments: true,
removeOptionalTags: true
})
.pipe(gulp.dest, distFolder);
return gulp.src(index)
.pipe($.useref())
.pipe($.rev())
.pipe($.revReplace({replaceInExtensions: ['.js', '.css', '.html', '.ejs']}))
.pipe($.if('*.html', saveHTML()));
}
One advantage that I'm familiar with is when it's used with assets, when you recompile the asset and create a new fingerprint for that file, the request won't return the cached response because it's a different file. As for your problem, you probably shouldn't be adding that has to your index, I think it's pretty unorthodox
So I'm having a slight problem with producing production ready scripts for my project. I'm using gulp to concatenate and minify my css and js, and while the css is working fine the gulp js function isn't generating my final file. Please refer to my code below:
gulp.task('js', function() {
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(gulp.dest(source + 'js'))
.pipe(rename({
basename: 'production',
suffix: '-min',
}))
.pipe(uglify())
.pipe(gulp.dest(source + 'js/'))
.pipe(notify({ message: 'Scripts task complete', onLast: true }));
});
If anyone has encountered a similar problem or has any tips it would be much appreciated :)
There is nothing wrong with your gulpfile. I tested it and it works perfectly.
The only thing I can guess is that your source is not set correctly. Did you forget the trailing slash '/' ?
I would suggest 2 things to figure it out. Include node path library to check where source is actually pointing to like this:
var path = require('path');
// in gulp task ...
path.resolve(path.resolve(source + 'js/app'));
Make sure it points where you think it does.
Secondly, you could use gulp-debug to establish that any files are found:
npm install gulp-debug
Then
var debug = require('gulp-debug');
// in gulp task ...
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(debug())
.pipe(gulp.dest(source + 'js'))
.pipe(debug())
// etc.
Good luck!
Based on additional infomation in the comments I realise you are generating JS files in a separate process ...
gulp is asynchronous by default. What this boils down to is that all functions try to run at the same time - if you want a specific order it must be by design. This is great because it's very fast but can be a headache to work with.
Problem
Here is what's basically happening:
// SOME TASK THAT SHOULD BE RUN FIRST
gulp.task('copy-vendor-files-to-tempfolder', function (done) {
// copy files to vendor folder
done()
})
// SOME TASKS THAT DEPEND ON FIRST TASK
gulp.task('complile-styles', function () { /* independent task */ })
gulp.task('concat-vendor-files', function () { /* concat files in vendor folder. depends on vendor files existing */ })
// GENERAL TASK WHICH STARTS OTHERS
gulp.task('ready', ['copy-vendor-files-to-tempfolder', 'compile-styles', 'concat-vendor-files])
When you try to run:
$ gulp ready
GULP TASK WILL FAIL! Folder is being created at the same time!!
NOWHERE TO COPY FILES!
Solution
There are many solutions but the following module has come in handy for me again and again:
npm install run-sequence
Then in your gulpfile.js:
var runSequence = require('run-sequence')
gulp.task('ready', function (done) {
runSequence(
'create-folders', // do this first,
[
'copy-css-files',
'copy-html-files'
], // do these AFTER but in parallel
done // callback when ready
)
})
This will guarantee the folder exists when you try to run the other functions.
In your specific case, you should make sure the task that concatenates the JS files is run after the task that copies them out of vendor.
Note: I'm leaving other answer because it contains useful help for debugging similar issues.
HTH!
I have browserify bundling up files and it's working great. But what if I need to generate multiple bundles?
I would like to end up with dist/appBundle.js and dist/publicBundle.js
gulp.task("js", function(){
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest("./dist"));
});
Obviously this isn't going to work since I am only specifying one output (bundle.js). I can accomplish this by repeating the above statement like so (but it doesn't feel right, because of the repetition):
gulp.task("js", function(){
browserify([
"./js/app.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest("./dist"));
browserify([
"./js/public.js"
])
.bundle()
.pipe(source("publicBundle.js"))
.pipe(gulp.dest("./dist"));
});
Is there a better way to tackle this? Thanks!
I don't have a good environment to test this in right now, but my guess is that it would look something like:
gulp.task("js", function(){
var destDir = "./dist";
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest(destDir))
.pipe(rename("publicBundle.js"))
.pipe(gulp.dest(destDir));
});
EDIT: I just realized I mis-read the question, there should be two separate bundles coming from two separate .js files. In light of that, the best alternative I can think of looks like:
gulp.task("js", function(){
var destDir = "./dist";
var bundleThis = function(srcArray) {
_.each(srcArray, function(source) {
var bundle = browserify(["./js/" + source + ".js"]).bundle();
bundle.pipe(source(source + "Bundle.js"))
.pipe(gulp.dest(destDir));
});
};
bundleThis(["app", "public"]);
});
gulp.task("js", function (done) {
[
"app",
"public",
].forEach(function (entry, i, entries) {
// Count remaining bundling operations to track
// when to call done(). Could alternatively use
// merge-stream and return its output.
entries.remaining = entries.remaining || entries.length;
browserify('./js/' + entry + '.js')
.bundle()
// If you need to use gulp plugins after bundling then you can
// pipe to vinyl-source-stream then gulp.dest() here instead
.pipe(
require('fs').createWriteStream('./dist/' + entry + 'Bundle.js')
.on('finish', function () {
if (! --entries.remaining) done();
})
);
});
});
This is similar to #urban_racoons answer, but with some improvements:
That answer will fail as soon as you want the task to be a dependency of another task in gulp 3, or part of a series in gulp 4. This answer uses a callback to signal task completion.
The JS can be simpler and doesn't require underscore.
This answer is based on the premise of having a known list of entry files for each bundle, as opposed to, say, needing to glob a list of entry files.
Multiple bundles with shared dependencies
I recently added support for multiple bundles with shared dependencies to https://github.com/greypants/gulp-starter
Here's the array of browserify config objects I pass to my browserify task. At the end of that task, I iterate over each config, browserifying all the things.
config.bundleConfigs.forEach(browserifyThis);
browserifyThis takes a bundleConfig object, and runs browserify (with watchify if dev mode).
This is the bit that sorts out shared dependencies:
// Sort out shared dependencies.
// b.require exposes modules externally
if(bundleConfig.require) b.require(bundleConfig.require)
// b.external excludes modules from the bundle, and expects
// they'll be available externally
if(bundleConfig.external) b.external(bundleConfig.external)
This browserify task also properly reports when all bundles are finished (the above example isn't returning streams or firing the task's callback), and uses watchify when in devMode for super fast recompiles.
Brian FitzGerald's last comment is spot on. Remember that it's just JavaScript!