I'm working with a Gulp file. In this file, I have two functions that perform two separate tasks. These functions are called using the following:
gulp.task('build', gulp.parallel(buildJs, buildCss));
When the build task is executed, I receive the following error:
The following tasks did not complete: default, build, buildCss
Did you forget to signal async completion?
My buildCss function is defined like this:
const files= [
{ input:'core.scss', output:'core.css' },
{ input:'theme.scss', output:'theme.css' },
{ input:'controls.scss', output:'controls.css'}
];
function buildCss() {
files.forEach(function(f) {
return gulp.src(`./scss/${f.input}`)
.pipe(sass())
.pipe(concat(f.output))
.pipe(gulp.dest('./'))
;
});
}
I suspect that each iteration of the loop is spinning up it's own thread. So, Gulp never knows when buildCss is finished. Which means, somehow, I need to know when all of the .css files are generated and call something. I'm unsure how to do the last piece if my understanding is correct though.
How do I address async completion of items in a loop in Gulp?
Thank you!
You can use .on('end') and create a Promise for each task, and then check that all promises went ok, sth like this should work:
function buildCss() {
return Promise.all(files.map(task => {
return new Promise((resolve,reject) => {
return gulp.src(`./scss/${task.input}`)
.pipe(sass())
.pipe(concat(task.output))
.pipe(gulp.dest('./'))
.on('end', () => resolve())
;
});
}));
}
I'm trying to wrap my head around gulp and modern JavaScript bundling.
The application is pretty simple:
A html page is displayed to the user, where he must check a few radio buttons.
User then submits his selections clicking on a button.
Upon choice submit, a JSON file must be parsed, in order to display one amongst 20 possible html pages
I have the following (simplified) structure:
dist/
index.html
src/
scripts/
main.js
modules/
dispatchForm.js
styles/
index.scss
templates/
index.pug
partials/
default.pug
selectors.pug
gulpfile.js
data.json
In my gulpfile.js, I have:
const bundler = () => {
return rollup({
input: './src/scripts/main.js',
plugins: [
babel(pkg.babel),
nodeResolve(),
commonJS(),
],
}).then((bundle) => bundle.write({
file: '.tmp/bundle.js',
format: 'umd',
sourceMap: 'inline',
}));
};
const uglify = () => {
return src('.tmp/bundle.js', {sourcemaps: true})
.pipe(plugins.uglify())
.pipe(dest('.tmp'))
.pipe(plugins.size({title: 'bundler minified'}));
};
const styles = () => {
const AUTOPREFIXER_BROWSERS = [
'ie >= 10',
'ie_mob >= 10',
'ff >= 30',
'chrome >= 34',
'safari >= 7',
'opera >= 23',
'ios >= 7',
'android >= 4.4',
'bb >= 10',
];
return src([
'src/styles/main.scss',
'src/styles/**/*.css',
])
.pipe(plugins.sassGlob())
.pipe(plugins.sass({
precision: 10,
}).on('error', plugins.sass.logError))
.pipe(plugins.autoprefixer(AUTOPREFIXER_BROWSERS))
.pipe(dest('.tmp'))
.pipe(plugins.if('*.css', plugins.cssnano()))
.pipe(dest('.tmp'))
.pipe(plugins.size({title: 'styles'}));
};
// Uses PUG as template
const templates = (env) => () => {
return src('./src/templates/*.pug')
.pipe(plugins.pug({locals: {
title: pkg.title,
description: pkg.description,
env,
}}))
.pipe(dest('dist'))
.pipe(plugins.size({title: 'templates'}));
};
const reload = (done) => {
server.reload();
return done();
};
const images = (env) => () => {
const destination = env === 'deploy' ? 'dist' : '.tmp';
return src('./src/images/**/*.{gif,jpg,png,svg}')
.pipe(dest(destination))
.pipe(plugins.size({title: 'size'}))
};
const serve = () => {
server.init({
notify: false,
logPrefix: 'WSK',
scrollElementMapping: ['main', '.mdl-layout'],
server: ['.tmp', 'dist'],
port: 3000,
});
watch(
['src/**/*.pug'],
series(templates('development'), reload)
);
watch(
['src/styles/**/*.{scss,css}'],
series(styles, templates('development'), reload)
);
watch(
['src/scripts/main.js', 'src/scripts/**/*.js'],
series(bundler, templates('development'), reload)
);
watch(
['src/images/**/*.{gif,jpg,png,svg}'],
series(images('development'), templates('development'), reload)
);
};
const clean = () => del(['.tmp', 'dist/*', '!dist/.git'], {dot: true});
exports.default = series(
clean,
bundler,
uglify,
styles,
templates('deploy'),
images('deploy')
);
exports.serve = series(
bundler,
styles,
templates('development'),
images('development'),
serve
);
The way I understand it, after cleaning the files, the bundler will:
Serve a html page in dist folder after compiling from my sources main.js, dispatchForm.js, scss and pug templates.
Main.js
import dispatchForm from './modules/dispatchForm';
const domContentLoad = (fn) => {
if (document.readyState !== 'loading') fn();
else document.addEventListener('DOMContentLoaded', fn);
};
domContentLoad(() => {
dispatchForm();
});
dispatchForm.js
const button = document.querySelector('[data-calculator-button]');
function updateValue() {
const gain = document.querySelector('[data-calculator-form][name="gain"]:checked');
const cost = document.querySelector('[data-calculator-form][name="cost"]:checked');
if (gain && cost) {
button.removeAttribute('disabled');
button.classList.remove('selectors__button--inactive');
} else {
button.setAttribute('disabled', '');
button.classList.add('selectors__button--inactive');
}
}
function dispatchForm() {
const radioInput = document.querySelectorAll('[data-calculator-form]');
radioInput.forEach(element => element.addEventListener('input', updateValue));
}
export default dispatchForm;
selectors.pug
...
.selectors__form
.selectors__radio
input.selectors__input(type='radio' id='gain-points' name='gain' value='points' data-calculator-form)
label.selectors__label(for='gain-points')
.selectors__radio
input.selectors__input(type='radio' id='gain-money' name='gain' value='money' data-calculator-form)
label.selectors__label(for='gain-money')
.selectors__form
for value in [70, 80, 90, 100, 110, 120, 130]
.selectors__radio
input.selectors__input(type='radio' id=`have-${value}` name='cost' value=value data-calculator-form)
label.selectors__label(for=`have-${value}`)
| Até
b= ` C$${value}`
button.selectors__button.selectors__button--calculate.selectors__button--inactive(disabled=true data-calculator-button)
...
The above creates some selectors for 'cost' or 'gain' from selectors.pug, main.js and dispatchForm.js, via gulps 'bundler', and renders it as html.
But now I would like to:
Use one of the two button submitted values (${value}) and pass it as an argument to a function that will parse a JSON file.
Finally, the parsed JSON result will be passed to another pug file
Questions
How do I get this JSON (from dispatchForm.js? from gulpfile.js? from pug natively?) and pass it to another pug template?
Should JSON fetching be dealt with on separate JS module, since displayed JSON will be rendered on a separate html page, using another pug template partial? How so?
Should all possible second page html files be generated at build time and JS be used to display only the one based on the submitted value? Or should this second html page be rendered dinamically?
gulp-data
I also learned that there are packages like gulp-data, used to handle json data, and I don't know if they are the way to go here.
Also, this SO question hints as how to pass pug JSON objects to client side JavaScript.
The way you've phrased this question makes me think your main issue is conflating the build step with the application "runtime" (as in, when users are using your application), like when dispatchForm.js would ever be run. Gulp is a tool to generate out your project, but this is meant to happen way before any users are interacting with your site.
The SO question you linked is having express render the pug pages at "runtime", which, architecturally, is pretty different from having it happen at the build step with gulp.
If I'm understanding what you want correctly, off the top of my head there's 3 main ways to do it. The first would be by having the client-side JS manipulate the dom and change the page appropriately. You could use pug for this, rendered into a JS library via something like rollup-plugin-pug (found via this SO answer).
The second would be having this be an api call to a server, which then renders a pug template (which is what the SO question you linked is doing).
Or third, you could do something like rendering out all possible pages you'd want your users to go to at build time, and just make the dispatchForm.js just be sending them to the appropriate pages. In this case I'd recommend defining out these options in one place (in say, a json file) and having that be the source of a gulp pipeline. It gets a little zany having gulp generate multiple files from a single file, but you can find a variety of ways people have done something similar like this github thread, this Stack Overflow answer, this gist, etc.
Edit
If you want stuff to happen "upon choice submit", that's way 1. So using rollup-plugin-pug, it would look something like (completely untested and off the top of my head):
//- template.pug
.content
p= gain
p= cost
// modules/calculateButton.js
import templateFn from '../../templates/partials/template.pug';
const button = document.querySelector('[data-calculator-button]');
button.addEventListener('click', (e) => {
if (e.target.getAttribute('disabled')) return;
const gain = document.querySelector('[data-calculator-form][name="gain"]:checked').value;
const cost = document.querySelector('[data-calculator-form][name="cost"]:checked').value;
document.getElementById("result").innerHTML = templateFn({
gain, cost
});
});
Otherwise nothing's parsed on submit. For examples of the 3rd way, I'd recommend checking out the links I sent above. A lot of build scripts is just finding your own way to do things that has the right amount of power/complexity balanced with ease of maintenance.
I have gulp file that I use to create the Html and Js for multiple kinds of exams, Ex1, Ex2 etc
Here's the task I use to create these for Ex1. It has hardcoded a call to the makeEx1Html task, three other tasks and followed by a function call where I can pass a parameter:
gulp.task('make_prod_ex1', function () {
runSequence(
'makeEx1Html',
'makeTemplate',
'rename_bundle_css',
'rename_bundle_js',
function () {
make_prod_index('ex1');
});
});
Here's the task that's hardcoded for Ex1:
gulp.task('makeEx1Html', function () {
return gulp.src(config.srcEx1Html, { base: process.cwd() })
.pipe(print(function (file) {
return "Found file " + file;
}))
.pipe(rename({ basename: 'base' }))
.pipe(gulp.dest('./'));
});
Here's the function where I can pass a parameter:
function make_prod_index(name) {
return function () {
gulp.src('index.html')
.pipe(htmlreplace({
'css': 'content/bundles/css.min.css',
'js': 'content/bundles/js.min.js'
}))
.pipe(eol())
.pipe(lec({ eolc: 'CRLF' }))
.pipe(replace('content/bundles/css.min.css', 'content/bundles/css-' + md5File('content/bundles/css.min.css') + '.min.css.gz'))
.pipe(replace('content/bundles/js.min.js', 'content/bundles/js-' + md5File('content/bundles/js.min.js') + '.min.js.gz'))
.pipe(rename('index-' + name + '.html'))
.pipe(gulp.dest('./'));
}
}
I would like to avoid having a specific task like 'makeEx1Html' and 'makeEx2Html' etc but I am not sure how to do this.
Note all these task needs to run in order which is why I'm using runSequence.
I would appreciate any suggestions. Ideally I would like the task that makes the Html to be a function that I can pass a parameter to but I am not sure how to fit this into my requirements.
Ideally I would like the task that makes the Html to be a function that I can pass a parameter to
You can do just that, except you don't just pass your parameters to the function, but also a callback cb that will be called when your function is done:
function makeExHtml(files, cb) {
return gulp.src(files, { base: process.cwd() })
.pipe(print(function (file) {
return "Found file " + file;
}))
.pipe(rename({ basename: 'base' }))
.pipe(gulp.dest('./'))
.on('end', cb);
}
In your gulp tasks you can then use the above makeExHtml() function and pass a callback that will execute the rest of your runSequence():
gulp.task('make_prod_ex1', function () {
makeExHtml(config.srcEx1Html, function() {
runSequence(
'makeTemplate',
'rename_bundle_css',
'rename_bundle_js',
function () {
make_prod_index('ex1');
});
});
});
I am developing intel cordova app...
to download files from server , i have included cordova file download plugin but it has data that i want to pass through variable...
here is my code:
var app = {
fileName: "PointerEventsCordovaPlugin.wmv", //<-- pass this value through variable (dynamic)
uriString: "http://media.ch9.ms/ch9/8c03/f4fe2512-59e5-4a07-bded-124b06ac8c03/PointerEventsCordovaPlugin.wmv", // <-- this one also
// Application Constructor
initialize: function() {
this.bindEvents();
},
....
I have added fileName and uristring.. but i want to add that value dynamically from variable.. how can i do this?????
cordova plugin link
please if you know anything about this than reply...
Following the example from the link you provided, remove the fileName and uriString fields from the app object, and parameterize the needed functions. For example, startDownload will become:
startDownload: function (fileName, uriString) {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
fileSystem.root.getFile(fileName, { create: true }, function (newFile) {
app.downloadFile(uriString, newFile);
});
});
},
I have an array of objects that looks like the following.
var bundles = [
{
src: 'js/my-component/*.js',
bundleName: 'my-component.js'
},
{
src: 'js/my-other-component/*.js',
bundleName: 'my-other-component.js'
}
]
I want the gulp task to process/concat every entry in the array, but it doesn't seem to work.
gulp.task('bundlejs', function(){
return bundles.forEach(function(obj){
return gulp.src(obj.src)
.pipe(concat(obj.bundleName))
.pipe(gulp.dest('js/_bundles'))
});
});
You should probably be merging the streams and returning the result, so that the task will complete at the appropriate time:
var es = require('event-stream');
gulp.task('bundlejs', function () {
return es.merge(bundles.map(function (obj) {
return gulp.src(obj.src)
.pipe(concat(obj.bundleName))
.pipe(gulp.dest('js/_bundles'));
}));
});
This solution does work, I had the wrong directory name. Doh.
https://github.com/adamayres/gulp-filelog helped me find the problem.