EDIT: it appears the pretty:true from Jade is from where the problem comes from. any idea why ?
I've got a bit of trouble here. Let me explain what I'm trying to do :
Here is what is supposed to happens:
file.jade > intermediate.html > index.html
The jade file outputs a html, then the html outpus a minified html. All in the watch function of course.
What I wrote to achieve this effect:
/* ===== TEMPLATES ===== */
gulp.task('templates', function() {
gulp.src(src + '/*.jade')
.pipe(jade({ locals: {data: data}, pretty:true}))
.pipe(rename('intermediate.html'))
.pipe(gulp.dest(src));
})
/* ===== HTML ===== */
gulp.task('html', function() {
// Get file
return gulp.src('./src/intermediate.html')
.pipe(inlineCss({
applyStyleTags: true,
applyLinkTags: true,
removeStyleTags: true,
removeLinkTags: true
}))
.pipe(inject(gulp.src([src + '/responsive.css']), {
starttag: '<!-- inject:head:{{ext}} -->',
transform: function (filePath, file) {
// return file contents as string
return file.contents.toString('utf8')
}
}))
.pipe(rename('index.html'))
.pipe(minifyHTML(options))
// Output file
.pipe(gulp.dest(archive))
.pipe(gulp.dest(dest));
});
/* ===== WATCH ===== */
gulp.task('watch', function() {
// Folders to watch and tasks to execute
gulp.watch([src + '/template.jade'], ['default']);
});
/* ===== DEFAULT ===== */
// Default gulp task ($ gulp)
gulp.task('default', function(callback) {
runSequence('clean',
'templates',
'html',
'notify',
callback);
});
Now what happens when a build happens ?
file.jade outputs intermediate.html
but index.html is produced with the OLD intermediate.html version, before its produced by the jade template. It's a bit tricky to explain.
What I mean is I need to do 2 gulp in a row every time to get the up to date index.html ! Because index.html takes the intermediate.html version that exists BEFORE the template is compiled from jade. I hope I'm being clear enough
I thought it would be ok with a runsequence but it doesn't seem to be !
I'm not a pro but those returns look problematic. If you're doing asynchronous stuff you need to use callbacks. It is likely skipping over the return statements altogether, or running them after everything else.
gulp.task('html', function() {
// Get file
gulp.src('./src/intermediate.html')
.pipe(inlineCss({
applyStyleTags: true,
applyLinkTags: true,
removeStyleTags: true,
removeLinkTags: true
}))
.pipe(inject(gulp.src([src + '/responsive.css']), {
starttag: '<!-- inject:head:{{ext}} -->',
transform: function (filePath, file) {
// return file contents as string
file.contents.toString('utf8')
}
}))
.pipe(rename('index.html'))
.pipe(minifyHTML(options))
// Output file
.pipe(gulp.dest(archive))
.pipe(gulp.dest(dest));
});
Disclaimer: I've never used gulp.
Also, is your console printing any errors or warnings?
This is pretty much a Grunt construction where you output to disk and pick it up in another task. You want to keep these things in one stream without writing the intermediate to disk in Gulp.
Since it all sequential anyway, why not run it through one pipeline? Template and html should be combined into one gulp task imo. The author of Gulp also confirms this here: How can I pipe stream between tasks #69
If you really want the logic to be separated, you could use lazypipe to construct two pipes beforehand (or even return them from a utility).
Option 2
If you're adamant about keeping these tasks as two parts or files, you can write them as two gulp plugins and pipe from one to another. Though this seems overengineering to me, it's a possibility. How to write a gulp plugin.
Related
I am trying to write a gulpfile as follows :
var gulp = require('gulp');
// other dependencies go here ....
// source of files
var inputs = require('./asset_source.js');
// a simple task
gulp.task('css', function () {
gulp.src(inputs.css)
.pipe(debug())
.pipe(plumber())
.pipe(maps.init())
.pipe(concatCss('libs.css'))
.pipe(maps.write('../srcmaps'))
.pipe(plumber.stop())
.pipe(gulp.dest('assets/css'));
});
// the watcher
gulp.task('watch', function () {
gulp.watch('./asset_source.js', ['css']);
});
// default task
gulp.task('default', ['browser-sync', 'watch']);
And the source of assets (asset_source.js) file is something like this :
module.exports = {
css: [
'path/to/a/file.css',
'path/to/another/file.css',
.......
.......
],
js: [
'path/to/a/file.js',
'path/to/another/file.js',
.......
.......
]
};
Now I run the app with by just typing gulp in the console and it starts in a browser thanks to browsersync. I have all the css,scss,js assets listed in the asset_source.js file which is in the same directory as the gulpfile.js. What I want to achieve is if I append or remove a value to/from either of the arrays in asset_source.js, the concerned task should run while the gulp is already running. In this case css should be running on any change to asset_source.js with its updated content.
But instead it doesnt do so. Even if there is a change in the asset_source file, gulp uses the initial content and runs the task. However if run gulp css in a separate terminal, it works.
Please help me where I am going wrong or if it is even possible. Thanks.
I managed to accomplish my task using a gulp plugin called gulp-insert like this:
gulp.task('compile-js', function () {
// Minify and bundle client scripts.
var scripts = gulp.src([
srcDir + '/routes/**/*.js',
srcDir + '/shared/js/**/*.js'
])
// Sort angular files so the module definition appears
// first in the bundle.
.pipe(gulpAngularFilesort())
// Add angular dependency injection annotations before
// minifying the bundle.
.pipe(gulpNgAnnotate())
// Begin building source maps for easy debugging of the
// bundled code.
.pipe(gulpSourcemaps.init())
.pipe(gulpConcat('bundle.js'))
// Buffer the bundle.js file and replace the appConfig
// placeholder string with a stringified config object.
.pipe(gulpInsert.transform(function (contents) {
return contents.replace("'{{{appConfigObj}}}'", JSON.stringify(config));
}))
.pipe(gulpUglify())
// Finish off sourcemap tracking and write the map to the
// bottom of the bundle file.
.pipe(gulpSourcemaps.write())
.pipe(gulp.dest(buildDir + '/shared/js'));
return scripts.pipe(gulpLivereload());
});
What I'm doing is reading our app's configuration file which is managed by the config module on npm. Getting our config file from server-side code is a snap using var config = require('config');, but we're a single-page app and frequently need access to the configuration settings on the client-side. To do that I stuff the config object into an Angular service.
Here's the Angular service before gulp build.
angular.module('app')
.factory('appConfig', function () {
return '{{{appConfigObj}}}';
});
The placeholder is in a string so that it's valid JavaScript for some of the other gulp plugins that process the file first. The gulpInsert utility lets me insert the config like this.
.pipe(gulpInsert.transform(function (contents) {
return contents.replace("'{{{appConfigObj}}}'", JSON.stringify(config));
}))
This works but feels a little hacky. Not to mention that it has to buffer the whole bundled file just so I can perform the operation. Is there a more elegant way to accomplish the same thing? Preferably one that allows the stream to keep flowing smoothly without buffering the whole bundle at the end? Thanks!
Have you checked gulp-replace-task?
Something like
[...]
.pipe(gulpSourcemaps.init())
.pipe(replace({
patterns: [{
match: '{{{appConfigObj}}}',
replacement: config
}],
usePrefix: false
})
.pipe(gulpUglify())
[...]
Admittedly, this feels a bit hacky, too, but maybe slightly better... I'm using envify and gulp-env in a React project. You could do something like this.
gulpfile.js:
var config = require('config');
var envify = require('envify');
gulp.task('env', function () {
env({
vars: {
APP_CONFIG: JSON.stringify(config)
}
});
});
gulp.task('compile-js', ['env'], function () {
// ... replace `gulp-insert` with `envify`
});
factory:
angular.module('app')
.factory('appConfig', function () {
return process.env.APP_CONFIG;
});
I've set up some simple Gulp tasks to process my CSS files.
The tasks are put together in one 'master' task:
gulp.task('process-css', ['concatCSS', 'minifyCSS', 'renameCSS']);
Just for reference, the definition of the concrete tasks follows:
gulp.task('minifyCSS', function() {
return gulp.src('themes/my_theme/css/dist/*.css')
.pipe(sourcemaps.init())
.pipe(minifyCSS())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('themes/my_theme/css/dist/'));
});
gulp.task('concatCSS', function() {
var files = [
'themes/rubbish_taxi/css/bootstrap.css',
'themes/rubbish_taxi/css/custom.css',
'themes/rubbish_taxi/css/responsive.css',
'themes/rubbish_taxi/css/jquery.fancybox.css'
];
return gulp.src(files)
.pipe(concat("bundle.css"))
.pipe(gulp.dest('themes/my_theme/css/dist/'));
});
gulp.task('renameCSS', function() {
gulp.src('themes/my_theme/css/dist/bundle.css')
.pipe(rename(function(path) {
path.basename += ".min";
}))
.pipe(gulp.dest("themes/my_theme/css/"));
});
The tasks complete without an error, but the problem is that minifyCSS does not minify the source file. An unminified version of the files is saved as bundle.min.css. I believe that the reason is that minifyCSS runs before concatCSS completed.
How can I make the tasks be executed synchronously?
Is my only option to specify which tasks should be executed before a give task like this:
gulp.task('minifyCSS', ['concatCSS'], function() {..} ?
It worked when I set it this way, but I wanted to avoid this to make code more readable.
gulp.task('minifyCSS', ['concatCSS'], function() {..} ?
It worked when I set it this way, but I wanted to avoid this to make code more readable.
More readable how? You're stating that minifyCSS is dependent on concatCSS. The line of code I quoted above is how you explain this dependency to gulp.
The alternative is to use something like run-sequence, but I think avoiding functionality built into the tool to solve the exact problem you're facing isn't justified by the desire for a subjective improvement in readability.
In order to build the final JS source for a web app, I am compiling JavaScript templates, CoffeScript sources and vanilla JS vendor scripts. A final task, dependent on the former three, would then concatenate the scripts together into one file.
The following setup, in which I define the last task with dependencies to the first three, the final step never executes. It seems that process doesn't wait until the partial files are written to the disc.
I tried merging event streams as well, but it seems to be an overkill for something like this. I'd appreciate if someone can point me in the right direction here.
// compile JS templates
gulp.task('js-build-template', function() {
gulp.src('./../app/assets/javascripts/**/*.jst.eco')
.pipe(eco({ basePath: 'app/assets/javascripts', namespace: 'JST_ATL' }))
.pipe(concat('_templates.js'))
.pipe(gulp.dest('public/assets/scripts/partials'));
});
// compile CoffeeScript source
gulp.task('js-build-source', function() {
gulp.src(js.source)
.pipe(coffee())
.pipe(concat('_source.js'))
.pipe(gulp.dest('public/assets/scripts/partials'));
});
// compile vendor scripts
gulp.task('js-build-vendor', function() {
gulp.src(js.vendor)
.pipe(concat('_vendor.js'))
.pipe(gulp.dest('public/assets/scripts/partials'));
});
// concatenate and fingerprint files
gulp.task('js-build', [ 'js-build-template', 'js-build-source', 'js-build-vendor' ], function() {
gulp.src([ 'public/assets/scripts/partials/_templates.js', 'public/assets/scripts/partials/_vendor.js', 'public/assets/scripts/partials/_source.js' ])
.pipe(concat('app.js'))
.pipe(gulp.dest('public/assets/scripts'))
.pipe(rev())
.pipe(gulp.dest('public/assets/scripts'))
.pipe(rev.manifest())
.pipe(gulp.dest('public/assets/scripts'));
});
in all 3 of your task you need return statements
gulp.task('js-build-template', function() {
return gulp.src('./../app/assets/javascripts/**/*.jst.eco')
.pipe(eco({ basePath: 'app/assets/javascripts', namespace: 'JST_ATL' }))
.pipe(concat('_templates.js'))
.pipe(gulp.dest('public/assets/scripts/partials'));
});
this will help in letting final task know that they have finished and it needs to start...
So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.