How can I use a template with a changing value for a grunt-contrib-copy cwd property

347 views Asked by At

I have a grunt build that should do the following:

  • Copy items from a location
  • Load a package.json file from those copied files and extract a version number
  • Copy items from a folder dependent on that version number

I have a task that reads the version number:

grunt.task.registerTask(
    "readVersionNumber",
    "Reads a version number",
    function(){
        var versionNumber = grunt.file.readJSON("pathToJson");
        grunt.config.set("versionNumber",versionNumber);
    }
);

(typed out rather than copy/paste so possibly some minor typos)

and I have a copy task with a cwd:

"cwd": "//networkPath/<%= versionNumber %>/docs"

But this is not working...

I have a very similar setup for replacing text in html / js files and looking at the grunt-text-replace.js file it uses grunt.template.process to replace the template in the string with the actual value. I suspect that for the copy task this is done before the copy task starts (before I have loaded and set the version number).

I can't find much info on how the cwd property is evaluated except that it seems to happen in the guts of grunt itself. It seems odd that core grunt functionality would not properly process templates but from what I can see it's not happening.

BTW - all this works fine if I use a template pointing to a value in my package.json file:

"cwd": "//networkPath/<%= pkg.path.to.version %>/docs"

Any help much appreciated

1

There are 1 answers

6
RobC On

You could consider:

  1. Dynamically obtaining the version frompackage.json whilst copying the items from your location, using the options.process function.
  2. Storing it as a property in an initially empty object.
  3. Then referencing it later in another copy task target.

The following gist seems to work:

Gruntfile.js

module.exports = function(grunt) {

    grunt.initConfig({

        // A 'version' property key and value (obtained via a copied package.json)
        // will be dynamically added to this empty object and referenced later.
        obtainedFromPackage: {},

        copy: {
            options: {
                // Avoid binaries getting corrupted. For more info see:
                // https://github.com/gruntjs/grunt-contrib-copy/issues/213
                noProcess: ['**/*.{png,gif,jpg,ico,pdf}']
            },
            initial: {
                files: [{
                    expand: true,
                    cwd: 'src/',
                    src: '**/*',
                    dest: './copiedItems/'
                }],
                options: {
                    // Use/abuse the 'options.process' function to obtain the 'version'
                    // value from the copied 'package.json'. This value is added to a
                    // 'version' property in the empty 'obtainedFromPackage' object.
                    process: function(content, srcpath) {
                        if (srcpath.indexOf('package.json') !== -1) {
                            var pkgVersion = grunt.file.readJSON(srcpath).version;
                            grunt.config('obtainedFromPackage.version', pkgVersion);
                        }
                        return content //Ensure original file content is not deleted.
                    }
                }
            },
            copyDocs : {
                files: [{
                    expand: true,
                    // Change directory to version obtained in previous copy task.
                    cwd: './networkPath/<%= obtainedFromPackage.version %>/',
                    src: 'docs/**',
                    dest: './copiedItems/'
                }]
            }
        }

    });

    grunt.loadNpmTasks('grunt-contrib-copy');

    grunt.registerTask('copyFiles', [
        'copy:initial',
        'copy:copyDocs'
    ]);

};

Note: This will obviously need adapting to suit your requirements. Particularly the path references!

Directory setup

The example gist in the Gruntfile.js assumes a fictitious project directory set up as follows:

project
├── Gruntfile.js (the one shown above)
├── networkPath
│  ├── 0.0.0
│  │  └── docs
│  │      ├── baz
│  │      │   └── baz.html
│  │      └── index.html
│  └── 1.1.1
│     └── docs
│         ├── foo
│         │   └── foo.html
│         └── index.html
├─── node_modules
│     └── ...
├─── package.json
└─── src
    ├─── a
    │   └─── b.js
    └─── package.json 

...Whereby the version property of package.json, (the one residing in the src folder), is set to:

"version": "1.1.1",

...and the folder named networkPath is what I'm assuming to be where your docs are stored.

Running the task

Execute the following command via the CLI:

$ grunt copyFiles

Resultant output

Running the task creates a new folder in the root project directory as follows:

project
├── copiedItems
│  ├── a
│  │  └── b.js
│  ├── docs
│  │  ├── foo
│  │  │   └── foo.html
│  │  └── index.html
│  └─── package.json
├─ ...
└─ ...

Points to note:

  1. All the items from the src folder (the one shown in the Directory setup section above) are copied to the newly created folder named copiedItems.

  2. The docs folder and its contents from the folder named 1.1.1 (the one shown in the Directory setup section above) are copied to the newly created folder named copiedItems. This particular folder is being copied because its parent directory name matches the version property of package.json being copied.

I hope this helps!


Update:

When adding some binary images to the src folder, I've since discovered that the resultant copied images were getting corrupted. See this issue logged in the grunt-contrib-copy repo for further info.

The Gruntfile.js above now utilizes the options.noprocess as a global in the copy task as a safeguard from this.

Image files are now copied successfully :)