grunt-contrib-copy is a Grunt plugin that provides comprehensive file copying capabilities with advanced configuration options. It offers pattern-based file selection using glob patterns, content processing during copy operations, permission and timestamp preservation, and flexible destination path handling.
npm install grunt-contrib-copy --save-devThis package is a Grunt plugin that registers itself when loaded:
// In your Gruntfile.js
grunt.loadNpmTasks('grunt-contrib-copy');// Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
copy: {
main: {
files: [
// Copy single files
{src: 'src/app.js', dest: 'build/app.js'},
// Copy with glob patterns
{expand: true, cwd: 'src/', src: ['**/*.js'], dest: 'build/'},
// Flatten directory structure
{expand: true, flatten: true, src: ['src/**/*.css'], dest: 'build/css/'}
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('default', ['copy']);
};grunt-contrib-copy is built around the standard Grunt plugin architecture:
grunt.file.copy, grunt.file.mkdir) for core operationsThe plugin operates in phases: configuration expansion → file filtering → directory creation → file copying → permission/timestamp handling → progress reporting.
Key Internal Utilities:
file-sync-cmp) to preserve timestamps only when file contents are identicalThe plugin registers a multi-task named 'copy' that processes file copying operations.
// Grunt plugin registration (internal)
grunt.registerMultiTask('copy', 'Copy files.', function() { ... });Configure source and destination files using Grunt's file mapping format.
interface FileConfig {
/** Source file patterns (glob patterns supported) */
src: string | string[];
/** Destination path */
dest: string;
/** Enable dynamic file expansion */
expand?: boolean;
/** Change working directory for src matching */
cwd?: string;
/** File/directory filter function or filter name */
filter?: 'isFile' | 'isDirectory' | ((filepath: string) => boolean);
/** Flatten directory structure in destination */
flatten?: boolean;
/** Error on non-existent source files */
nonull?: boolean;
/** Original configuration (used internally) */
orig?: {
expand?: boolean;
};
}Configure copy behavior with comprehensive options.
interface CopyOptions {
/** File encoding for reading/writing files (default: grunt.file.defaultEncoding, typically 'utf8') */
encoding?: string;
/** Content transformation function */
process?: (content: string, srcpath: string) => string;
/** @deprecated Use process instead */
processContent?: (content: string, srcpath: string) => string;
/** Files to exclude from processing (glob patterns) */
noProcess?: string | string[];
/** @deprecated Use noProcess instead */
processContentExclude?: string[];
/** Preserve file timestamps during copy */
timestamp?: boolean;
/** Set file/directory permissions (boolean to copy existing, string/number for specific mode) */
mode?: boolean | string | number;
}Usage Example:
copy: {
main: {
options: {
// Set specific encoding (utf8, ascii, binary, base64, hex, etc.)
encoding: 'utf8',
// Transform file contents during copy
process: function(content, srcpath) {
return content.replace(/{{VERSION}}/g, '1.0.0');
},
// Exclude binary files from processing
noProcess: ['**/*.{png,gif,jpg,ico,psd}'],
// Set file permissions
mode: '0644',
// Preserve timestamps
timestamp: true
},
files: [
{expand: true, cwd: 'src/', src: ['**'], dest: 'build/'}
]
}
}Transform file contents during the copy operation.
/**
* Content transformation function
* @param content - The file content as a string
* @param srcpath - The source file path
* @returns Transformed content string
*/
type ProcessFunction = (content: string, srcpath: string) => string;Usage Example:
copy: {
templates: {
options: {
process: function(content, srcpath) {
// Replace placeholders with actual values
return content
.replace(/\{\{TITLE\}\}/g, 'My Application')
.replace(/\{\{VERSION\}\}/g, grunt.option('version') || '1.0.0');
}
},
files: [
{src: 'templates/*.html', dest: 'build/', expand: true, flatten: true}
]
}
}Control which files are copied using built-in or custom filters.
/**
* File filter function
* @param filepath - The file path to test
* @returns true to include the file, false to exclude
*/
type FilterFunction = (filepath: string) => boolean;
/** Built-in filter options */
type FilterOption = 'isFile' | 'isDirectory' | FilterFunction;Usage Examples:
copy: {
// Copy only files (exclude directories)
filesOnly: {
files: [
{expand: true, src: ['src/**'], dest: 'build/', filter: 'isFile'}
]
},
// Custom filter function
jsFiles: {
files: [
{
expand: true,
src: ['src/**'],
dest: 'build/',
filter: function(filepath) {
return filepath.match(/\.js$/);
}
}
]
}
}Configure how directories and file paths are handled during copying.
interface PathHandlingOptions {
/** Change working directory for source pattern matching */
cwd?: string;
/** Flatten directory structure (place all files in dest root) */
flatten?: boolean;
/** Dynamic file expansion for glob patterns */
expand?: boolean;
}Usage Examples:
copy: {
// Copy maintaining directory structure
preserve: {
files: [
{expand: true, cwd: 'src/', src: ['**'], dest: 'build/'}
]
},
// Flatten all files to destination root
flatten: {
files: [
{expand: true, flatten: true, src: ['src/**/*.js'], dest: 'build/js/'}
]
},
// Copy single file tree
tree: {
files: [
{expand: true, src: 'assets/**', dest: 'build/'}
]
}
}Control file permissions and timestamp preservation.
interface FileAttributeOptions {
/** File/directory permission mode */
mode?: boolean | string | number;
/** Preserve file access and modification times */
timestamp?: boolean;
}Usage Examples:
copy: {
// Copy existing permissions
withPerms: {
options: {
mode: true
},
files: [
{expand: true, src: ['scripts/**'], dest: 'build/'}
]
},
// Set specific permissions
executable: {
options: {
mode: '0755'
},
files: [
{src: 'bin/myapp', dest: 'build/bin/myapp'}
]
},
// Preserve timestamps
timestamped: {
options: {
timestamp: true
},
files: [
{expand: true, src: ['docs/**'], dest: 'build/docs/'}
]
}
}Control error behavior for missing files and other issues.
interface ErrorHandlingOptions {
/** Generate error for non-existent source files */
nonull?: boolean;
}Usage Example:
copy: {
strict: {
options: {
nonull: true // Will error if source files don't exist
},
files: [
{src: 'required-file.txt', dest: 'build/required-file.txt'}
]
}
}When the copy task runs, it:
files arrayExample output:
Running "copy:main" (copy) task
Created 3 directories, copied 12 files
Done, without errors.copy: {
// Copy and process HTML templates
html: {
options: {
process: function(content, srcpath) {
return content.replace(/\{\{env\}\}/g, process.env.NODE_ENV || 'development');
}
},
files: [
{expand: true, cwd: 'src/', src: ['**/*.html'], dest: 'build/'}
]
},
// Copy static assets without processing
assets: {
options: {
noProcess: ['**/*.{png,jpg,gif,svg,woff,ttf}']
},
files: [
{expand: true, cwd: 'src/assets/', src: ['**'], dest: 'build/assets/'}
]
},
// Copy and flatten vendor libraries
vendor: {
files: [
{expand: true, flatten: true, src: ['node_modules/*/dist/*.min.js'], dest: 'build/vendor/'}
]
}
}copy: {
dev: {
options: {
// Fast copying for development
timestamp: false
},
files: [
{expand: true, cwd: 'src/', src: ['**'], dest: 'build/'}
]
},
prod: {
options: {
// Optimized copying for production
process: function(content, srcpath) {
// Minify or transform content
return content.replace(/\s+/g, ' ').trim();
},
timestamp: true,
mode: '0644'
},
files: [
{expand: true, cwd: 'src/', src: ['**/*.{js,css,html}'], dest: 'build/'}
]
}
}