I'm building a Grunt plugin, and I need to select one of the targets in the Gruntfile.js.
Gruntfile.js:
grunt.initConfig({
plugin: {
options: {},
target: {} // <= This is what I want to select
}
});
Inside of tasks/plugin.js I can use options = this.options() to get the options object, but I can't just use this.target. Any thoughts?
Grunt only exposes this.target for multi-tasks. To access the same value for regular tasks (bar in grunt foo:bar), access the args array instead: this.args[0]
Update:
Previous answer was written according to the official terminology by Grunt. Re-reading your question, you'll access the property within the config object using the grunt.config() method, drilling down to the specific properties you're trying to access within the grunt config object. Based on your grunt.initConfig above, you'd access the contents of target with grunt.config('plugin.target');
Related
I have a Gruntfile.js through which i'm invoking mochaTest using grunt-mocha-test module. I can pass an argument/parameter to the gruntTask from command line but i'm struggling to pass the same parameter into the spec file running via the above module. The code looks like below,
mochaTest: {
test: {
options: {
reporter: 'spec',
quiet: false,
clearRequireCache: false,
clearCacheFilter: (key) => true,
noFail: false
},
src: [
'test/createSpec.js'
]
}
}
Task is registered like below,
grunt.registerTask('e2etest', function(scope) {
console.log(scope); // logs user/session based on the parameter passed
grunt.task.run('mochaTest');
});
// Above task is invoked like,
grunt e2etest:user
(or)
grunt e2etest:session
I need to pass this value (user/session) into mochaTest so it can be accessed inside the spec file. Fundamentally the aim is to run the createSpec.js file both for user and session, this values is parametrized inside the spec file and based on the value passed the suite would run.
Is there a possibility to do this? Please advise.
Please refer to this issue for detail, and I think the solution you need here is:
node <node flags here> node_modules/mocha/bin/_mocha <mocha arguments here>
You can utilize nodes process.argv to read the argument (i.e. user or session) from within the file named createSpec.js.
To better understand how, follow these steps:
At the top of createSpec.js add the following line of code:
console.log(process.argv);
Then run grunt e2etest:user via your CLI and you should see the following logged to your console:
[ 'node', '/usr/local/bin/grunt', 'e2etest:user' ]
Note: the information you want is positioned at index two of the array.
Now, delete the line we just added which reads console.log(process.argv); from createSpec.js.
createSpec.js
So, the steps above (1-3) illustrated that the arguments (user or session) can be accessed in createSpec.js utilizing process.argv. In which case you could do something like the following inside createSpec.js.
const argument = process.argv[2].split(':').pop();
if (argument === 'user') {
// Run `user` specific tests here...
} else if (argument === 'session') {
// Run `session` specific tests here...
}
Note, we're using process.argv[2].split(/:/).pop(); to extract either user or session from the array item positioned at index two, whose initial value will be either e2etest:user or e2etest:session respectively.
Gruntfile
Your createSpec.js file is now somewhat dependent on the grunt task named e2etest being invoked correctly. For example, if a user were to run grunt e2etest without providing the arguments then createSpec.js is not going to do much.
To enforce the correct usage of the e2etest task (i.e. it must be run using either grunt e2etest:user or grunt e2etest:session), you could change your task in your Gruntfile as follows:
grunt.registerTask('e2etest', function(scope) {
if (!scope || !(scope === 'user' || scope === 'session')) {
grunt.warn(`Must be invoked with: ${this.name}:user or ${this.name}:session`);
}
grunt.task.run('mochaTest');
});
The gist above initially checks that an argument has been provided and is either user or session. If the argument is incorrect or missing then grunt.warn is utilized to warn the user.
If your version of nodejs does not support ES6 Template literals then use grunt.warn as follows instead:
grunt.warn('Must be invoked with: ' + this.name + ':user or ' + this.name + ':session');
Additional comment
The code/gist shown in the createSpec.js section above will work if your use-case is exactly as you mention in your question. I.e. you invoke via the commandline using grunt e2etest:user or grunt e2etest:session. However, if that changes and you cannot guarantee that e2etest:user or e2etest:session will be exactly positioned at index two of the process.argv array, then you may need to do the following at the top of createSpec.js instead:
// Return the value in the array which starts with
// `e2etest` then extract the string after the colon `:`
const argument = process.argv.filter(arg => {
return arg.match(/^e2etest:.*/);
}).toString().split(':').pop();
if (argument === 'user') {
// Run `user` specific tests here...
} else if (argument === 'session') {
// Run `session` specific tests here...
}
I'm currently trying to learn gruntjs for dev and production build.
I want to assign a global config variable to determine stuff.
I have a simple initConfig :
grunt.initConfig({
foo: {
bar: {GLOBAL: true},
baz: {GLOBAL: false}
}
});
grunt.registerTask('one', ['foo:bar']);
grunt.registerTask('two', ['foo:baz']);
My question is:
What exactly is the colon in my tasks doing? (foo:bar or foo:baz)
And what is the difference between a colon and a simple dot?
My Goal is to have a global variable set either to true or false for further processing:
grunt.initConfig({
foo: {
bar: {GLOBAL: true},
baz: {GLOBAL: false}
},
awesomestuff: {
smth: GLOBAL ? 'yes' : 'no',
another: !Global ? 'DoDebug' : 'MakeRelease'
}
});
grunt.registerTask('one', ['foo:bar', 'awesomestuff']);
grunt.registerTask('two', ['foo:baz', 'awesomestuff']);
How would I achieve this?
Update
I got the global variable working somehow. By registering a new new task called init with an argument I can call it in an other task.
grunt.registerTask('init', 'Init', function(param) {
grunt.config('GLOBAL', param)
});
grunt.registerTask('one', ['init:true', 'foo:bar', 'awesomestuff']);
In this case the init task will be called with the vairable param set to true.
But the question is still:
Why would I use a colon insted of a dot to reference an object?
Why would I use a colon instead of a dot to reference an object?
To understand why, you firstly need to understand grunt task configurations and targets.
Single Target
To help you further understand this concept and terminology, take a look at this example configuration for a grunt plugin called grunt-contrib-copy. It's a plugin that copies files. Below is a snippet of that code:
grunt.initConfig({
copy: { // <-- Task
main: { // <-- Target
// ... <-- other configurations go here.
}
}
});
In this example above the Task is named copy and it includes a single Target named main.
To register this Task you would do so as follows:
grunt.registerTask('copyFiles', ['copy:main']);
and you would enter the following via your command line to run it:
$ grunt copyFiles
Multiple Targets
Grunt Tasks can also include more than one Target. Consider this example code below:
grunt.initConfig({
copy: {
js: {
// ... <-- Additional configurations for this Target go here.
},
css: {
// ... <-- Additional configurations for this Target go here.
}
}
});
You could register the example above as follows:
grunt.registerTask('copyJavaScriptFiles', ['copy:js']);
grunt.registerTask('copyCssFiles', ['copy:css']);
So, via the command line:
Running $ grunt copyJavaScriptFiles will copy all the JS files according to the configurations specified.
Running $ grunt copyCssFiles will copy all the CSS files according to the configurations specified.
If you wanted to copy both the JS and CSS files you could register a task as follows:
grunt.registerTask('copyAll', ['copy']);
And you would run it by entering $ grunt copyAll in your command line.
Notice in the last example it does not include any colon :. Grunt this time will run all the Targets in the copy Task, namely the js one and the css one.
And what is the difference between a colon and a simple dot?
Colon
Hopefully by now you can see what the colon : does. It is used to reference a particular Target within a Task and is typically only used when a Task has multiple Targets and you want to specifically reference one of them.
Simple dot
The simple dot is JavaScript's standard notation to access properties of an object. Google "JavaScript notation" to find out more about Dot Notation and Square Bracket Notation.
Within the context of your Gruntfile.js the dot notation is typically used to call the functions/methods/properties of the grunt object. For example:
grunt.initConfig({...});
grunt.loadNpmTasks(...);
grunt.registerTask(...);
EDIT 1 Updated the answer after the original post/question was updated.
So here is my problem :
Currently, I have a dozen of functions related to WEBRTC within a template js file. My objective is to have those functions in a separate file, called webRTCWrapper.js for example, and to call those functions in my template without using global variable.
I think I must use namespaces, am I correct ?
If so, how do you use them ?
EDIT : For anyone interested, this is exactly what I was looking for :
http://themeteorchef.com/snippets/using-the-module-pattern-with-meteor/
Make a directory called packages/ parallel to your .meteor/ directory. You can create a package that exports a single object/function. On the command line, use meteor create --package <yourpackagename> and meteor add <yourpackagename> You can edit the js file to add a namespace.
MyNamespace = {};
MyNamespace.myFunction = function () { };
Then, in the package.js, simply export that namespace.
api.export('MyNamespace');
You can use a common pattern of having a global object and your functions inside that object.
Greetings = {
hello: function(name) { return "Hello "+name+" how are you?"; }
}
And then you can call it inside the template helpers :
Template.GreetingsTemplate.helpers({
sayHello: function() { return Greetings.hello('Maxence'); }
})
Take note of the loading order of files in Meteor, anything inside the lib folders is loaded first. If you run into problems where "Greetings" object is not defined, then its because that file was not loaded already.
Edit:
You can reuse the same pattern for adding more functions in different files (you could use App = App || {} but it will throw error in Chrome for example).
App = (typeof App === 'undefined')? {} : App;
App.someFunction = function(){};
or even, if you use underscore.js:
App = (typeof App === 'undefined')? {} : App;
_.extend(App, {
someFunction: function(){}
});
Since now the regular way to use the code from another file was going through a global (server and client). As Joao suggested you can make your own global App variable where you will store or more generically a global MODULE one (basically the same solution as Joao but with explanation).
But with with the arrival of ES2015 support, we will very soon be able to have an official pattern to achieve this. However as the 1.2 does not supports yet the import/export syntax:
Note, The ES2015 module syntax (import/export) is not supported yet in Meteor 1.2.
If you want to start using those features earlier, I would recommend using this package which is an temporary solution to fill the current import/export gap, the meteor team development are currently looking for an elegant solution to support this.
I am working on a Angular Demo Application and I want to automatize a lot of things.
It's some sort of a boilerplate, albeit a more complex one, and I want to make a config file in which we'll put API Keys and other stuff, and I want that file to be populated by Grunt with user interaction when the project is started for the first time.
Something like:
grunt build - it should ask the user directly in the console for the API keys, that will be inserted in the config file where I am defining some global constants for the entire App.
Is there such an example of functionality with Grunt ?
You can handle the questioning by using:
https://github.com/dylang/grunt-prompt
It is a nice little plugin that do one job and do it well. It put whatever value you have entered in the command line into variables: (example)
prompt: {
target: {
options: {
questions: [
{
config: 'key', // arbitrary name or config for any other grunt task
type: 'input', // list, checkbox, confirm, input, password
message: 'What is your API key?',
default: '', // default value if nothing is entered
when: function(answers) { return !grunt.file.exists('config.yml'); } // only ask this question when this function returns true
}
]
}
}
}
Then you can use the Grunt.file functions to write those values into files:
http://gruntjs.com/api/grunt.file#grunt.file.write
To orchestrate it, you will need to create a custom task: (example)
grunt.registerTask("my_config_task", function (arg) {
var key = arg || grunt.config('key');
grunt.file.write("config.yml", key);
});
grunt.registerTask('build', ['prompt', 'my_config_task']);
The writing will likely need refinement as you will, I guess, need to replace values and organise as a yml file or json object, etc...
Found one of the possible solutions while looking at the sources of grunt-bump. What are they doing is parsing the config file as a JSON object:
https://github.com/darsain/grunt-bumpup/blob/master/tasks/bumpup.js#L128
Replacing whatever values they need (as JSON) and overwrite the file with the object stringified:
https://github.com/darsain/grunt-bumpup/blob/master/tasks/bumpup.js#153
Seems to work well.
I am a newbie to web development but have been playing around with YUI for a few months now. Can anyone let me know how to load a custom "js" script in YUI 3?
I want to use the "contentflow" carousel in YUI 3. For this i need to include the contentflow.js within the "YUI.use()" so that I can access the methods.
To add a module (so that YUI recognises it) you need to add it to the configuration. There are three ways of doing this.
Using YUI_config = {}; sets a global configuration object for all YUI().use
Using YUI.GlobalConfig = {}; sets a global configuration object for all YUI().use
Using YUI({}).use(...; sets a local configuration object for this YUI().use
In the config object you need to configure the module to be understood in your use.
{
filter : "raw",
modules : {
"contentFlow" : {
fullpath : "path/to/contentFlow.js"
}
}
}
Then you can do:
YUI().use("contentFlow", function (Y) {
//content flow available here
});
However I would recommend using the YUI.add method in the content flow JavaScript to expose the content flow "class". So in contentFlow.js, I would wrap the following:
YUI.add("contentFlow", function (Y) {
//contentFlow.js contents goes here...
...
//end of file
Y.ContentFlow = ContentFlow;
}, '', {});
Then you can:
YUI().use("contentFlow", function (Y) {
var cf = new Y.ContentFlow({...});
});