NodeJs: external javascript with dependencies - javascript

We are trying to use only nodeJS with minimal dependencies to other packages, the challenge we now encounter is HandelbarsJS. We found a package, Assemble who can generate html for us. Only, it is very very slow, about 3 seconds each time, of these 3 seconds, there are 2,5 / 2,7 seconds of the next line:
var assemble = require('assemble');
Our package.json script section:
"scripts": {
"build:handlebars": "node scripts/handlebars.js",
"watch:handlebars": "nodemon --watch assets --exec \"npm run build:handlebars\"" }
the script/handlebars.js file
#! /usr/bin/env node
var assemble = require('assemble');
var extname = require('gulp-extname');
console.log(Date.now() - start);
assemble.data('assets/templates/data/*.json');
assemble.layouts('assets/templates/layouts/*.hbs');
assemble.partials('assets/templates/partials/*.hbs');
assemble.src('assets/templates/*.hbs', { layout: 'default' })
.pipe(extname())
.pipe(assemble.dest('build/'));
Each time, when we save a .hbs file, Nodemon restart and the external javascript file will be called.
How can we ensure that 'require' get called only once, or whether they remain in memory?
Thank you!

Since you want to accomplish using this with assemble, but without gulp, I recommend chokidar.
npm install chokidar --save
Now you can require chokidar like this:
var chokidar = require('chokidar');
Then define a little helper that runs handler whenever something in a pattern changes:
function watch(patterns, handler) {
chokidar.watch(patterns, {
ignoreInitial: false
}).on('add', handler).on('change', handler).on('unlink', handler);
}
Now we can alter the script like this:
#! /usr/bin/env node
var assemble = require('assemble');
var extname = require('gulp-extname');
var chokidar = require('chokidar');
console.log(Date.now() - start);
assemble.data('assets/templates/data/*.json');
assemble.layouts('assets/templates/layouts/*.hbs');
assemble.partials('assets/templates/partials/*.hbs');
// Enable --watch command line for Chokidar, otherwise, just run!
if (process.argv.pop() === '--watch') {
watch('assets', runOnce);
} else {
runOnce();
}
function watch(patterns, handler) {
chokidar.watch(patterns, {
ignoreInitial: false
}).on('add', handler).on('change', handler).on('unlink', handler);
}
function runOnce() {
assemble.src('assets/templates/*.hbs', { layout: 'default' })
.pipe(extname())
.pipe(assemble.dest('build/'));
}
And instead of nodemon, this will keep your script alive and running. So, in npm, you want this:
"scripts": {
"build:handlebars": "node scripts/handlebars.js",
"watch:handlebars": "node scripts/handlebars.js --watch"
}
Whenever a file changes, the script will now run, without re-invoking from scratch.

The beta version of assemble is based on gulp and has a cli that you can use just like you would use gulp, but if you don't want to use the cli and use npm scripts instead, you can do something based on #roel-van-uden's answer without chokidar and also be able to reload the actual assets (e.g. data, layouts, partials)
#! /usr/bin/env node
var start = Date.now();
var assemble = require('assemble');
var extname = require('gulp-extname');
assemble.task('assets', function () {
console.log(Date.now() - start);
assemble.data('assets/templates/data/*.json');
assemble.layouts('assets/templates/layouts/*.hbs');
assemble.partials('assets/templates/partials/*.hbs');
return assemble.src('assets/templates/*.hbs', { layout: 'default' })
.pipe(extname())
.pipe(assemble.dest('build/'));
});
assemble.task('watch', ['assets'], function () {
assemble.watch('./assets/**/*.*', ['assets]');
});
// Enable --watch command line
if (process.argv.pop() === '--watch') {
assemble.run(['watch']);
} else {
assemble.run(['assets']);
}

Related

ReferenceError: $ is not defined (WebdriverIO)

I'm launching native apps with the help of WebdriverIO and mocha, but unable to communicate with the device, but able to launch the application but not interact with the element.
android_app_test.js
const webdriverio = require('webdriverio');
const androidOptions = require('../../../helpers/caps').androidOptions;
const assert = require('chai').assert;
androidOptions.capabilities.appPackage = "com.google.android.calculator"
androidOptions.capabilities.appActivity = "com.android.calculator2.Calculator"
describe('Create Chrome web session', function () {
let client;
before(async function () {
client = await webdriverio.remote(androidOptions)
});
after(async function () {
await client.deleteSession();
});
it('should create and destroy Android browser session', async function () {
const elem = await $('#digit_2')
elem.waitForDisplayed(3000);
await client.touchClick('digit_2');
});
});
config.js
var Mocha = require('mocha'), fs = require('fs');
var mocha = new Mocha({
reporter: 'mochawesome-screenshots',
reporterOptions: {
reportDir: 'customReportDir',
reportName: 'customReportName',
reportTitle: 'customReportTitle',
reportPageTitle: 'customReportPageTitle',
takePassedScreenshot: true,
clearOldScreenshots: true,
shortScrFileNames: true,
jsonReport: false,
multiReport: false
},
timeout: 600000,
})
var file = ['./test/basic/app/']; //location of the test js
for (var i = 0; i < file.length; i++) {
fs.readdirSync(file[i]).forEach(function (filename) {
mocha.addFile(file[i] + filename);
});
}
mocha.run(function (failures) {
process.on('exit', function () {
process.exit(failures);
});
});
package.json
"scripts": {
"test": "mocha config.js"
},
Not sure about that, i think something was wrong in my configuration or else
The $ global is added through the WebdriverIO test runner. Since you're using wdio through standalone mode, you don't get access to those globals. Try this instead:
const elem = await client.$('#digit_2')
Make sure you're using the newest version of Webdriver.io. Webdriver.io v5 is the latest version that also implements the $('selector') shortcut.
If you're using Webdriver.io v4 - you may still need to use browser.element('selector') to find your elements.
It appears from the tags in your question, and the code you posted you maybe on version 4.
$ is usually used as a shorthand to run JQuery functions (such as your $('#digit_2'), in the "android_app_test.js" file).
From the WebdriverIO's doc:
The $ command is a short way to call the findElement command in order to fetch a single element on the page. It returns an object that with an extended prototype to call action commands without passing in a selector. However if you still pass in a selector it will look for that element first and call the action on that element.
To fix this you have to install JQuery with this commands:
In a terminal run:
npm install --save jquery
npm install --save-dev #types/jquery
then import it at the top of your "android_app_test.js" file like this
import * as $ from "jquery";

gulp command to take parameters

my package.json has scripts like this
{
"scripts": {
"pretest": "npm run tsc",
"test": "gulp e2e",
}
}
we use typescript and webdriverIO for automation. I want to use gulp so that i can pass parameters to my test framework. Example:
npm test --suite HomePageTests
then the specs related to Home page must run.
I have the gulp file like this
// gulpfile.js
const gulp = require('gulp');
const Launcher = require('webdriverio/build/lib/launcher');
const wdio = new Launcher(path.join(__dirname,
'src/config/conf.ts'));
// fetch command line arguments
const arg = (argList => {
let arg = {}, a, opt, thisOpt, curOpt;
for (a = 0; a < argList.length; a++) {
thisOpt = argList[a].trim();
opt = thisOpt.replace(/^\-+/, '');
if (opt === thisOpt) {
// argument value
if (curOpt) arg[curOpt] = opt;
curOpt = null;
}else {
// argument name
curOpt = opt;
arg[curOpt] = true;
}
}
console.log("arg", arg)
return arg;
})(process.argv);
gulp.task('e2e', () => {
return wdio.run(code => {
process.exit(code);
}, error => {
console.error('Launcher failed to start the test',error.stacktrace);
process.exit(1);
});
});
So when I call gulp directly like
gulp e2e --suite HomePageTests
it gets printed as
suite: HomePageTests
But if i use
npm test --suite HomePageTests
It fails as it prints gulp e2e HomePageTests
questions
How do I pass these values from npm to make gulp understand
If I am pass to another value like gulp e2e --server staging and would like to use the variable "staging" in my spec file like
if server=== staging{
// do this
} else {
// do that
}
How should I pass them from gulp file to my spec file?
Thanks!!
You could use the yargs dependence
var argv = require('yargs').argv;
gulp.task('test', function(){
console.log(argv.arg);
});
then if you run a command on a gulp passing the arg like this
gulp test --arg HomePageTests
it will output on console HomePageTests

Cordova / Gulp best practice

I'm trying to setup a Cordova develop/deployment chain using Gulp. I ended with this gulpfile.js, but I'm not really satisfied since I need to kill "gulp watch" task in order to run "gulp deploy" task.
var gulp = require('gulp'),
gutil = require('gulp-util'),
exec = require('gulp-exec');
var spawn = require('child_process').spawn;
var stripDebug = require('gulp-strip-debug');
var uglify = require('gulp-uglify');
/**
* Config ogj
*/
var config = {
jsDir: 'www/assets/js',
jsDirBrowser: 'platforms/browser/www/assets/js',
production: !!gutil.env.production
};
/**
* Automatically run 'cordova prepare browser' after any modification
* into the www directory - really useful for development/deplyment purpose
*
* #see watch task
*/
gulp.task('prepare', function () {
gutil.log('Prepare browser');
var options = {
continueOnError: false, // default = false, true means don't emit error event
pipeStdout: false, // default = false, true means stdout is written to file.contents
customTemplatingThing: "test" // content passed to gutil.template()
};
var reportOptions = {
err: true, // default = true, false means don't write err
stderr: true, // default = true, false means don't write stderr
stdout: true // default = true, false means don't write stdout
}
return gulp.src('./**/**')
.pipe(exec('cordova prepare browser', options))
.pipe(exec.reporter(reportOptions));
});
/**
* Watch for changes in www
*/
gulp.task('watch', function () {
gulp.watch('www/**/*', ['prepare']);
});
/**
* Default task
*/
gulp.task('default', ['prepare']);
/**
* Javascript production depolyment.
*/
gulp.task('deploy-js', function () {
gutil.log('Deploy');
return gulp.src(config.jsDir + '/*.js')
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest(config.jsDirBrowser));
});
/**
* Production deployment
* To be run before uploading files to the server with no gulp instaces running
*/
gulp.task('deploy', ['deploy-js']);
Which could be a best practice for develop and deply a Cordova project using Gulp?
[EDIT]
I think the problem is in the "prepare" task: it never returns, probably due a gulp-exec issue, but I really don't know how to debug it.
From what I've understood the only issue is that gulp command does not return control to you for you to execute gulp deploy
the prepare task never returns because of the behavior of the watch feature - you've passed control to watch the files so it will return only when you stop watching. It is the expected behavior and not a probably due a gulp-exec issue.
The solution I would adopt in this situation is to run the gulp task in the background, using the native nohup gulp & so that the watching is moved to the background for me to execute deploy.

How to use IBMIoTF for node.js in a WebApplication?

I tested the IBMIoTF in a node.js server and it worked well.
IBMIoTF you can find here: https://www.npmjs.com/package/ibmiotf
Now I want to use the IBMIoTF in a WebApplication and I notice this little note in the documentation: https://www.npmjs.com/package/ibmiotf#load-the-library-in-browser
Load the library in browser
load iotf-client-bundle.js or iotf-client-bundle-min.js from the dist directory
I also took a look into the http://browserify.org/, but I am not able to get it working.
It is able to load the library in the index.html
<script src="libs/iotf/iotf-client-bundle.min.js"></script>
, but how can I create a object instance in the angular module?
Option 1
I am not able to use require in a WebApplication.
var config = {
"org": "THEORG",
"id": "IOT_WEB_APPLICATION",
"auth-key": "THEKEY",
"auth-token": "THETOKEN",
"type" : "shared"
};
var IotClient = require('ibmiotf');
var iotClient = new IotClient.IotfApplication(config);
In this situation I get
angular.js:14110 ReferenceError: require is not defined
Option 2
I also tried to use a object, I found in iotf-client.js file.
module.exports = {
IotfDevice: _IotfDevice['default'],
IotfManagedDevice: _IotfManagedDevice['default'],
IotfGateway: _IotfGateway['default'],
IotfManagedGateway: _IotfManagedGateway['default'],
IotfApplication: _IotfApplication['default']
};
and did a implementation like this in my controller:
var config = {
"org": "THEORG",
"id": "IOT_WEB_APPLICATION",
"auth-key": "THEKEY",
"auth-token": "THETOKEN",
"type" : "shared"
};
var iotClient = new IotfApplication(config);
Here I get:
angular.js:14110 ReferenceError: IotfApplication is not defined
These options didn't work, but how to create a instance for the IBMIoTF?
Can anyone help me?
You need to browserify the ibmiotf as part of your buildprocess:
1. in your package.json add dependency to ibmiotf npm
2. do npm install
3. add a script command to your package.json for browserify/uglify like this
"scripts": {
"build": "browserify your.js | uglifyjs -m -c warnings=false > bundle.js"
}
do npm build, this will produce a bundle.js with all your javascript files and the dependencies specified to bundle.js
Include the bundle.js in your web html file. ...<script src="bundle.js"></script>
in "your.js" do something like this
var config = require(YOURCONFIG);
var deviceType = "YOURDEVICETYPE";
var appClient = new client.IotfApplication(config);
appClient.connect();
appClient.on("connect", function () {
console.log("Connected");
appClient.subscribeToDeviceEvents(deviceType);
});
appClient.on("deviceEvent", function (deviceType, deviceId, eventType, format, payload) {
console.log("Device Event from :: "+deviceType+" : "+deviceId+" of event "+eventType+" with payload : "+payload);
});

Yeoman: install dependencies from cache

I want to add an option for when debugging my generator or when working offline that will download npm and bower stuff from cache (by using --cache-min 999999 and --offline respectively).
Currently, this is my code (which both installs the dependencies and calls grunt bower):
CallumGenerator.prototype.installDeps = function () {
var cb = this.async();
this.installDependencies({
skipInstall: this.options['skip-install'],
callback: function () {
this.spawnCommand('grunt', ['bower'])
.on('close', function () {
cb();
});
}.bind(this)
});
};
It looks like I'll most likely have to call .npmInstall() and .bowerInstall() manually in order to specify options (I think?), but I don't know how to specify any options. To clarify, this is how I would do it in the console:
npm install --cache-min 999999 --save-dev grunt-contrib-less
bower install --offline --save jquery#1.10.2
You can't specify options directly from #installDependencies see: https://github.com/yeoman/generator/blob/master/lib/actions/install.js#L44-L69
You can specify them for both #npmInstall and bowerInstall https://github.com/yeoman/generator/blob/master/lib/actions/install.js#L121-L143
The options you pass are in the form of an object hash and will be parsed by dargs node modules, so you should follow the module conventions for declaring options
The code I used, which should be fine for anyone to use (you might want to get rid of the final callback, though):
CallumGenerator.prototype.installDeps = function () {
var cb = this.async();
this.npmInstall(null, {
skipInstall: this.options['skip-install'],
cacheMin: this.cachedDeps ? 999999 : 0
}, function () {
this.bowerInstall(null, {
skipInstall: this.options['skip-install'],
offline: this.cachedDeps
}, function () {
this.spawnCommand('grunt', ['bower'])
.on('close', function () {
cb();
});
}.bind(this));
}.bind(this));
};
It works fine. this.cachedDeps will define whether the cache is used or not.

Categories