Gulp 4 - How to generate dynamic tasks based on two arrays - gulp

I'm building an e-mail generation pipeline (multiple templates) using nunjucks and json translation files. This means I need to loop over the multiple templates and the translation files, however I can't seem to get it working.
Tried adding another loop inside the templates.map(), but that doesn't seem to be working (or I'm doing it completely wrong ofcourse). It almost works, but it crashes at some point, generating only a few of the templates. The first template works, but it crashes at the second template:
The following tasks did not complete: <anonymous>
Did you forget to signal async completion?
source: https://cobwwweb.com/dynamic-tasks-gulp-4
var templates = [];
var languages = ["nl", "en"];
function generateTemplates(done) {
const tasks = templates.map((template) => {
return () => {
const langs = languages.map((lang) => {
return () =>
gulp.src(`source/templates/${template}`)
.pipe(data(function () {
return require(`./source/translations/${lang}/${template.split('.')[0] }.json`);
}))
.pipe(nunjucksRender({
path: ['source/partials']
}))
.pipe(gulp.dest('dist/' + lang));
});
return gulp.series(...langs, (seriesDone) => {
seriesDone();
})();
}
});
return gulp.series(...tasks, (seriesDone) => {
seriesDone();
done();
})();
}
I also tried generating tasks using 2 for-loops, but this only generates the last template of the array of the last language in the array (example: only en/template2 will be generated correctly). I do see in the console that the tasks are starting and finishing, but I don't see them anywhere. Maybe the loop is finished mush faster than the generation of tasks? :
var templates = fs.readdirSync('./source/templates');
var languages = ["nl", "en"];
for (var lang of languages) {
for (var template of templates) {
gulp.task(`${lang}-${template}`, function (done) {
return gulp.src(`source/templates/${template}`)
.pipe(data(function () {
return require(`./source/translations/${lang}/${template.split('.')[0]}.json`);
}))
.pipe(nunjucksRender({
path: ['source/partials']
}))
.pipe(gulp.dest(`dist/${lang}`));
});
tasks.push(`${lang}-${template}`);
}
}
gulp.task('genlang', gulp.series(tasks));
My folder structure:
/dist
/source
--/partials
--/templates
--/template1.html
--/template2.html
--/translations
--/en
--/template1.json
--/template2.json
--/nl
--/template1.json
--/template2.json

Fixed it myself, I needed to have some done cb's in the returns:
function generateTemplates(done) {
const tasks = templates.map((template) => {
return (doneTasks) => {
const langs = languages.map((lang) => {
return (doneLanguages) => {
gulp.src(`source/templates/${template}`)
.pipe(data(() => require(`./source/translations/${lang}/${template.split('.')[0]}.json`)))
.pipe(nunjucksRender({
path: ['source/partials']
}))
.pipe(gulp.dest('./dist/' + lang));
doneLanguages();
}
});
return gulp.parallel(...langs, (seriesDone) => {
seriesDone();
doneTasks();
})();
};
});

Related

how to mock on global object in backstop.js/puppetter

So backstop.js provides ability to run custom script against underlying engine. I use puppeteer as an engine so I try to mock Date.now with 'onReadyScript':
page.evaluate('window.Date.now = () => 0; Date.now = () => 0;');
...
page.addScriptTag({
// btw `console.log` here is not executed, do I use it in wrong way?
content: 'Date.now = () => 0;'
});
...
page.evaluate(() => {
window.Date.now = () => 0;
Date.now = () => 0;
});
Last one, I think, is modifying Date in context of Node, not inside the puppeteer, but anyway tried that as well.
Nothing worked, script under the test still output real Date.now. Also I checked Override the browser date with puppeteer but it did not help me.
Yes, I know I'm able to skip particular selectors, but it does not always make sense(think about clock with arrows).
After trying onBeforeScript with evaluateOnNewDocument() it works for me. Complete script:
module.exports = async function (page, scenario) {
if (!page.dateIsMocked) {
page.dateIsMocked = true
await page.evaluateOnNewDocument(() => {
const referenceTime = '2010-05-05 10:10:10.000';
const oldDate = Date;
Date = function(...args) {
if (args.length) {
return new oldDate(...args);
} else {
return new oldDate(referenceTime);
}
}
Date.now = function() {
return new oldDate(referenceTime).valueOf();
}
Date.prototype = oldDate.prototype;
})
}
};
Reason: onReadyScript is executed when page under testing has already been loaded and executed. So code is bound to original Date by closure, not the mocked version.

Table to JSON using node.js

I am trying to convert a table to JSON, to search for data easily, the URL is: http://www.tppcrpg.net/rarity.html
I found this package:
https://www.npmjs.com/package/tabletojson
I tried to use it like:
'use strict';
const tabletojson = require('tabletojson');
tabletojson.convertUrl(
'http://www.tppcrpg.net/rarity.html',
{ useFirstRowForHeadings: true },
function(tablesAsJson) {
console.log(tablesAsJson[1]);
}
);
However it returns undefined in the console, are there any alternative options or am I using the package wrong?
Hey you are actually getting data, change the console.log
Your output have total one array only but you are putting tablesAsJson[1] in console, but array index starts with [0].
'use strict';
const tabletojson = require('tabletojson');
tabletojson.convertUrl(
'http://www.tppcrpg.net/rarity.html',
function(tablesAsJson) {
console.log(tablesAsJson[0]);
}
);
For better looking code:
const url = 'http://www.tppcrpg.net/rarity.html';
tabletojson.convertUrl(url)
.then((data) => {
console.log(data[0]);
})
.catch((err) => {
console.log('err', err);
}); // to catch error

How do I load a WASM module in a Vue component without initializing the module every time?

I have created a Rust library of type cdylib using
cargo web build --target=wasm32-unknown-unknown
I use a modified version of the "rust-wasm-loader" NPM package to build and load the WASM file. rust-wasm-loader uses this as a way to use the Rust code:
const wasm = require('./main.rs')
wasm.initialize().then(module => {
// Use your module here
const doub = module.cwrap('doub', 'number', ['number'])
console.log(doub(21))
})
I do not want to initialize the module every time I want to use the code. How do I load the module and use it like a library?
Since the loading of WebAssembly is asynchronous and may actually take some time for large modules, you need to handle the state when the module is not loaded, and then let the rest of the application know when the WebAssembly module is loaded.
You do not say how you are handling state in your Vue application, but if you are e.g. using Vuex you can do something like this:
const doubPlugin = store => {
wasm.initialize().then(module => {
const doub = module.cwrap('doub', 'number', ['number'])
store.subscribe((mutation, state) => {
if (mutation.type === 'DOUB_REQUEST') {
store.commit('DOUB_RESULT', doub(mutation.payload))
}
})
store.commit('DOUB_READY')
})
}
const store = new Vuex.Store({
state,
mutations,
plugins: [doubPlugin]
})
I've done a similar thing in an Elm/WebAssembly application (relevant JavaScript), so if you want to see how this can be applied in practice you can check that out.
Making a wrapper JS module that performs initialization and re-exports the promise seems like the most straightforward approach.
// main.js
module.exports = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
Then anything can do
require("./main.js").then(api => {
console.log(api.doub(21));
});
and will always get the same module. Or alternatively you could invert the async part and do
// main.js
const api = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
exports.doub = async function (val) {
return (await api).doub(val);
};
Then users of your module could do
const api = require("./main.js");
api.doub(21).then(result => {
console.log(result);
});
I created a class to wrap the WebAssembly loading and created a cwrap for every function:
class mkLib {
ready = false
_mod = require("./main.rs").initialize().then(module => {
this._mod = module
this.doub = module.cwrap('doub', 'number', ['number'])
this.ready = true
})
}
export default mkLib
In the Vue component's data there is a variable for the new class and in watch I wait for a change in the ready property:
data () {
return {
mod: new mkLib,
ready: false
}
},
watch: {
'mod.ready': function () {
this.ready = true
// now this.mod.FUNC(PARAMS) can be used
console.log(this.mod.doub(20))
}
}

Vue.js dynamic component loading + hot reload

I have Laravel 5.3.21 application with Vue.js 1.0.28
I'm using hot-reload workflow with browserify-hmr plugin.
Here is the simple gulpfile.js used to achieve that:
var elixir = require('laravel-elixir');
var gutil = require('gulp-util');
require('laravel-elixir-browserify-official');
require('laravel-elixir-vueify');
// If 'gulp watch' is run
if (gutil.env._.indexOf('watch') > -1) {
// Enable watchify for faster builds
elixir.config.js.browserify.watchify.enabled = true;
// Add the browserify HMR plugin
elixir.config.js.browserify.plugins.push({
name: 'browserify-hmr',
options: {
url: 'http://1.2.3.4:2096',
hostname: '1.2.3.4',
port: 2096
}
})
}
elixir.config.js.browserify.watchify.options.poll = true;
elixir(function (mix) {
mix.copy('node_modules/datatables.net-bs/css/dataTables.bootstrap.css',
'resources/assets/css/vendor/dataTables.bootstrap.css');
mix.styles([
'vendor/dataTables.bootstrap.css'
]);
mix.sass('app.scss');
mix.browserify('app.js');
});
I need to load components dynamically from the resources/assets/js/views/ folder, so I could make my front-end code modular and based on current route name $Laravel->routeName = request()->route()->getName() in Laravel.
For example:
// Global Settings.
Route::get('admin/settings', 'Admin#settings')
->name('admin_global_settings');
Then in resources/assets/js/views/admin/admin_global_settings.js I have code to initialize Vue.js component and register it with Vue.js instance:
var FeaturedOpportunities = require( '../../components/Featured-Opportunities.vue' );
window.Vue.component('FeaturedOpportunities', FeaturedOpportunities);
That is all nice but here is the problem in resources/assets/js/app.js:
window.Vue = require('vue');
require('vue-resource');
Vue.http.interceptors.push((request, next) => {
request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
next();
});
// Problem is here, have to keep track of all my routes and their corresponding modules:
var routes = {
'organization_invites_roles': function () {
require('./views/organization/organization_invites_roles');
},
'admin_global_settings': function () {
require('./views/admin/admin_global_settings');
},
};
// Is there a way to load it dynamically?
if (Laravel.routeName) {
if (routes[Laravel.routeName]) {
routes[Laravel.routeName]();
}
}
new Vue({
el: 'body',
components: {
},
ready() {
console.log('Vue and Vueify all set to go!');
}
});
I've found some way that could probably solve this issue: Compiling dynamically required modules with Browserify but not sure if this applicable for my case.

Get "file basename" in gulp-replacer-task

I'm manipulating a set of files and I am using gulp-replacer-task to replace the content of processed files with "strings" based on the basename or path of the file currently in the pipe-line.
How do i get at the file's properties currently in the pipe-line ?
gulp.task('svgbuild', function() {
return gulp.src('./src/*.svg')
.pipe(replace({
patterns: [
{
match: /STRING_TO_MATCH/g,
replacement: function() {
// how to get the basename of the "file" in the stream;
var str = 'file.basename'
// manipulate to get replacement string based on basename
var repl = str.toUpperCase()+'-inc'
return repl;
}
}
]
}))
});
Somewhat later than I hoped, but it appears I found a solution for the problem using gulp-tap. This is what my gulpfile.js looks like:
var gulp = require('gulp'),
path = require('path'),
replace = require('gulp-replace-task'),
tap = require('gulp-tap');
gulp.task('svgbuild', function () {
return gulp.src('*.txt')
.pipe(tap(function (file) {
return gulp.src(file.path)
.pipe(replace({
patterns: [
{
match: /foo/g,
replacement: function () {
var ext = path.extname(file.path),
base = path.basename(file.path, ext);
return base.toUpperCase() + '-inc'
}
}
]
}))
.pipe(gulp.dest('build'));
}));
});
I think you must look for the solution in Node.js. Maybe this helps: https://nodejs.org/api/path.html?