===== A Simple Grunt Task ===== Now that we have all the setup done, let's set up a Grunt task. In this example, and all the later ones, we'll assume you have a terminal window open at the top level of your project folder. If you're not sure how to do that, double-check [[Setting Up a Project]]. For starters, let's say you want to get a Twine story you've been editing into the **src** folder -- let's say it's called "My Story." To do this, we'll need another Grunt plugin to help. Type ''npm install %%--%%save grunt-contrib-copy'' in your terminal window and press Enter. As you might guess from its name, this plugin copies files from one place to another. ==== Editing the Gruntfile ==== Now, we'll need to create a file named **Gruntfile.js**. This file is where we tell Grunt about the tasks we want it to run for us. Grunt always looks for this file name, with this capitalization, at the top level of your project folder. Let's start with the bare minimum for a working Gruntfile. In this example, and any others, you don't need to retype what's on the page. Just select the filename tab and it'll download to your computer. module.exports = function(grunt) { require('load-grunt-tasks')(grunt); }; Now type ''grunt'' in your terminal window and press Enter. If everything is correct, then Grunt will respond: Warning: Task "default" not found. Use --force to continue. Aborted due to warnings. * If instead you see ''A valid Gruntfile could not be found'', then make sure your **Gruntfile.js** is named correctly, and at the top level of your project, not in **src**. * If instead you see ''>> SyntaxError: missing ) after argument list'' or anything starting with ''SyntaxError'', double-check the contents of **Gruntfile.js**. It's likely you are missing a parenthensis or other punctuation. Even though our Gruntfile is valid, Grunt still gives us a warning because we haven't actually defined any tasks yet. We'll fix that in a moment, but first a quick discussion of what's in this file. Although you can treat this as boilerplate, it can be helpful to understand what's going on, even at a basic level. A Gruntfile always exports a single function to the outside world -- that's what line 1 does. If you're not familiar with JavaScript functions, think of them as just a set of steps that are performed in sequence. Grunt calls this function as part of its setup process. The second line uses a helper module, [[https://www.npmjs.com/package/load-grunt-tasks|load-grunt-tasks]] that does the drudgework of setting up any Grunt plugins we are using. In this case, it makes Grunt aware of the ''grunt-contrib-copy'' plugin that we installed. ==== Adding a Task ==== Now, let's define a task. Replace your Gruntfile with: module.exports = function(grunt) { require('load-grunt-tasks')(grunt); grunt.initConfig({ copy: { fromTwine: { src: '/Users/Me/Documents/Twine/Stories/My Story.html', dest: 'src/' } } }); grunt.registerTask('default', ['copy']); }; Now, when you run grunt, it should say: Running "copy:fromTwine" (copy) task Done. But nothing will be copied just yet. Before we talk about why, let's talk about what we've added in this version. ''grunt.initConfig()'' is the heart of a Gruntfile. It'll be where we define most of our tasks. It always takes a single JavaScript object as an argument. The object's top-level properties correspond to the Grunt plugins we have installed. In this case, the ''grunt-contrib-copy'' plugin uses the ''copy'' property for its tasks. Inside that property, we define the individual tasks that make use of the plugin. We only have one, called ''fromTwine'', but you can have as many as you like. Inside the ''fromTwine'' task, we say what we want to copy (the ''src'' property) and where it should go (the ''dest'' property). Then we define the default task. The task named ''default'' is run if you just type ''grunt'', as we have been doing. (If you'd like to run a specific task, you could type ''grunt copy:fromTwine'', or if you'd like to run all tasks belonging to a plugin, ''grunt copy''.) The reason why we write ''grunt.registerTask('default', ['copy'])'' instead of ''grunt.registerTask('default', 'copy')'' is that it's possible to make the default task run several tasks in sequence. For example, if we wanted to copy several files than run the tasks belonging to an imaginary plugin named ''printFiles'' by default, we could write ''grunt.registerTask('default', ['copy', 'printFiles'])'' instead. It's OK if you don't follow this completely right away. As we go through other examples of Gruntfiles, these ideas will become clearer. ==== Actually Copying a File ==== The reason right now why nothing is happening is that we haven't given Grunt the complete path to your Twine story file. To do that, open Twine and choose **Show Library** from the **Twine** menu. This will open the folder on your computer where Twine automatically saves your stories as you work. Update **Gruntfile.js** with this path. On OS X and Linux, it will look similar to the example above: module.exports = function(grunt) { require('load-grunt-tasks')(grunt); grunt.initConfig({ copy: { fromTwine: { src: '/Users/Me/Documents/Twine/Stories/My Story.html', dest: 'src/' } } }); grunt.registerTask('default', ['copy']); }; On Windows, it'll look more like: module.exports = function(grunt) { require('load-grunt-tasks')(grunt); grunt.initConfig({ copy: { fromTwine: { src: 'C:/Users/Me/Documents/Twine/Stories/My Story.html', dest: 'src/' } } }); grunt.registerTask('default', ['copy']); }; If you are using Windows, you should always change the backslashes (e.g. %%\%%) in paths to forward slashes (e.g. /) when using Grunt. If your Gruntfile is set up correctly, running ''grunt'' will say: Running "copy:fromTwine" (copy) task Copied 1 file Done. Congratulations! You've got the basics down. How did we know about ''grunt-contrib-copy'', and that it was looking for ''src'' and ''dest'' properties? Grunt maintains a [[http://gruntjs.com/plugins|searchable plugin registry]] of its own. Chances are, for whatever task you can think of, there is a Grunt plugin to help you.