427

I'm trying to move from Gulp to Webpack. In Gulp I have task which copies all files and folders from /static/ folder to /build/ folder. How to do the same with Webpack? Do I need some plugin?

Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
Vitalii Korsakov
  • 45,737
  • 20
  • 72
  • 90
  • 2
    Gulp is great to understand. just call webpack from gulpfile.js if you want – Baryon Lee Mar 16 '17 at 03:15
  • If you're using Laravel Mix, https://laravel.com/docs/5.8/mix#copying-files-and-directories is available. – Ryan Apr 22 '19 at 18:43
  • Many of these answers are now out of date. [`file-loader` is now deprecated as of webpack 5](https://v4.webpack.js.org/loaders/file-loader/). The recommended way is to use [asset-modules now](https://webpack.js.org/guides/asset-modules/), as per [this answer](https://stackoverflow.com/a/67068576/542251) – Liam Jan 09 '23 at 14:43

13 Answers13

711

Requiring assets using the file-loader module is the way webpack is intended to be used (source). However, if you need greater flexibility or want a cleaner interface, you can also copy static files directly using my copy-webpack-plugin (npm, Github). For your static to build example:

const CopyWebpackPlugin = require('copy-webpack-plugin');
 
module.exports = {
    context: path.join(__dirname, 'your-app'),
    plugins: [
        new CopyWebpackPlugin({
            patterns: [
                { from: 'static' }
            ]
        })
    ]
};

Compatibility note: If you're using an old version of webpack like webpack@4.x.x, use copy-webpack-plugin@6.x.x. Otherwise use latest.

floer32
  • 2,190
  • 4
  • 29
  • 50
kevlened
  • 10,846
  • 4
  • 23
  • 17
  • 15
    This is so much simpler when you want to copy an entire directory (ie. static html, and other boilerplate images)! – Arjun Mehta Nov 22 '15 at 19:31
  • 6
    Did the trick, thank you :) gave up on file loader after several failed attempts to get it to do a very simple command. your plugin worked first time. – arcseldon Dec 16 '15 at 16:09
  • This is almost what I want. Any way to strip off the paths / flatten? (that is, while using a wildcard / globbing) – jacobq Mar 01 '16 at 21:19
  • you can assign a 'to' field, unless you meant flatten the subfolders inside static, which I don't think is currently possible eg. ``` plugins: [ new CopyWebpackPlugin([ { from: 'static/things', to: 'static' } ]) ] ``` – psimyn Apr 01 '16 at 23:23
  • @iX3, it's possible to flatten the paths (with globs) starting in v2.1.0 – kevlened Apr 16 '16 at 21:29
  • 1
    Require node.js version >= 0.12 – Anthony Raymond May 11 '16 at 09:19
  • 1
    This plugin does not re-copies files when they change. I.e. no watch functionality. Answer with file-loader provides that out of the box. – Yan Jul 15 '16 at 07:06
  • 4
    @Yan The plugin does re-copy files if they change (dev-server or webpack --watch). If it's not copying for you, please file an issue. – kevlened Jul 15 '16 at 15:33
  • How can you specify something like 'copy only the .html files'? – Claudiu Constantin Feb 01 '17 at 14:20
  • @kevlened Simple Readme/Documentation just like this answer will be very much helpful – Vikas Sardana Feb 19 '17 at 18:26
  • 4
    I am new to webpack, but I am having a hard time understanding why we need to use file-loader/url-loader/img-loader ... instead of just copying them? What is the benefit that we gain from doing this with, say, file-loader? – BreakDS Mar 20 '17 at 02:00
  • 1
    It is possible to chain this with a loader someway? Let's say I want to copy a directory of svg images and I would want to optimize them when moving them with some kind of image loader? – Stephan-v Apr 06 '17 at 12:53
  • 2
    Since you are the plugin author. There is no better pace to ask this question. Using "copy-webpack-plugin" plugin... can I filter the files from source directory so that it only copies the file with certain file extencion ex. copy only ".html" ? Regards – DevWL Jun 11 '17 at 00:17
  • 1
    @kevlened: you might update your answer to include write-file-webpack-plugin (required since v3 for writing to filesytem when webpack-dev-server is running) – sfletche Nov 29 '17 at 23:35
  • @Erowlin - Node v0.10 was still supported when I originally answered this question. Node v0.10 didn't support `const`, so `var` was used for compatibility. In this case, either one works, but I'll update it to make it consistent with modern examples. – kevlened Mar 27 '18 at 15:31
  • Conflict with `clean-webpack-plugin`, how can I make `copy-webpack-plugin` working at the end of the building? – Suge May 21 '18 at 05:45
  • copy-webpack-plugin does not preserve timestamps so deployment scripts can not tell what has changed. – Alien Technology May 02 '19 at 15:36
  • 2
    thank you for actually answering the actual question posed in the title. – Gavin Jun 05 '19 at 17:38
  • thats an extremely bad solution. the recommended loaders will take care of compression and bundling a-lot of them together so there will be less http calls to files with less size. – Stav Alfi Nov 11 '19 at 22:07
  • You sir, saved my life. I explicitely needed this for a config solution related with docker and different production environments. Cheers! – Javi Marzán Jul 01 '20 at 10:05
  • This is no longer the correct usage as CopyWebpackPlugin now takes an object rather than an array as argument: `new CopyWebpackPlugin({ patterns: [{ from: 'static' }] })` – Maffelu Jul 26 '20 at 06:05
197

You don't need to copy things around, webpack works different than gulp. Webpack is a module bundler and everything you reference in your files will be included. You just need to specify a loader for that.

So if you write:

var myImage = require("./static/myImage.jpg");

Webpack will first try to parse the referenced file as JavaScript (because that's the default). Of course, that will fail. That's why you need to specify a loader for that file type. The file- or url-loader for instance take the referenced file, put it into webpack's output folder (which should be build in your case) and return the hashed url for that file.

var myImage = require("./static/myImage.jpg");
console.log(myImage); // '/build/12as7f9asfasgasg.jpg'

Usually loaders are applied via the webpack config:

// webpack.config.js

module.exports = {
    ...
    module: {
        loaders: [
            { test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, loader: "file" }
        ]
    }
};

Of course you need to install the file-loader first to make this work.

Johannes Ewald
  • 17,665
  • 5
  • 44
  • 39
  • 48
    "_Of course you need to install the file-loader first to make this work._" Link to the aforementioned "file-loader" [here](https://github.com/webpack/file-loader). And [here](http://webpack.github.io/docs/using-loaders.html) is how to install and use it. – Nate Jul 27 '15 at 20:50
  • 1
    Here's you can install file-loader ```npm install file-loader --save-dev``` – Dmitry Davydov Oct 30 '15 at 13:46
  • 25
    You still have the problem of HTML files and all references in them not being loaded. – kilianc Dec 14 '15 at 08:46
  • 2
    Can I use those images in my css? I tried with url() in css but it didn't work. Should I change the routes? – DaNeSh Jul 06 '16 at 16:04
  • Do you bundle your css with webpack + css-loader + file-loader? Then url() references should work. – Johannes Ewald Jul 07 '16 at 11:45
  • 145
    yeah, if you want to get in the hell of webpack plugins, you can use file-loader, css-loader, style-loader, url-loader, ... and then you can have a great time configuring it the way you need and googling and not-sleeping :) or you can use copy-webpack-plugin and get your job done... – Kamil Tomšík Aug 13 '16 at 19:51
  • 12
    @KamilTomšík So your recommendation is that we should use a webpack plugin to avoid webpack plugins? (Just kidding. I got your point.) – Konrad Viltersten Feb 03 '17 at 10:01
  • 3
    The problem is that doesn't work all the time and I'm not even sure it answers the question depending on how you think about "static" files. Everyone always forgets about files not referenced by your code. What about, for example, JSON files you load asynchronously? Those won't be seen with an import/require. Or anything dynamically used where a specific file path was not imported? There's a copy webpack plugin, but it would be nice to see something directly handled by webpack. – Tom Feb 16 '17 at 19:52
  • 1
    Your test would be better written.. `/\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/` – Molomby Mar 02 '17 at 01:36
  • 14
    Ok, most part of all images are in css and html. So should i require all these images in my JS files using require('img.png'); to make it work with that file-loader? That's quite crazy thing. – Rantiev Apr 16 '17 at 17:09
  • Just require your html with the html-loader and it will try to load all dependencies with the file-loader. – Johannes Ewald Apr 17 '17 at 19:20
  • i mean, yeah, this works great if you don't actually need to copy files... but... when you do actually need to copy static files into your build folder... – Kevin B Jun 13 '17 at 16:27
  • 1
    If you don't want to handle some assets with webpack, you can use the copy-webpack-plugin to copy assets into your public folder—or just a regular copy command outside of webpack at all. – Johannes Ewald Jul 12 '17 at 07:37
  • 1
    File-loader is now deprecated and has been [replaced with asset modules](https://webpack.js.org/guides/asset-modules/) – Liam Jan 09 '23 at 14:40
62

If you want to copy your static files you can use the file-loader in this way :

for html files :

in webpack.config.js :

module.exports = {
    ...
    module: {
        loaders: [
            { test: /\.(html)$/,
              loader: "file?name=[path][name].[ext]&context=./app/static"
            }
        ]
    }
};

in your js file :

  require.context("./static/", true, /^\.\/.*\.html/);

./static/ is relative to where your js file is.

You can do the same with images or whatever. The context is a powerful method to explore !!

  • 4
    I prefer this method over the copy-webpack-plugin module. Additionally, I was able to get it working without using "&context=./app/static" in my webpack config. I only needed the require.context line. – Dave Landry Mar 28 '16 at 18:58
  • 2
    I'm trying this, it seems great but for one little problem I'm getting, which is that it is putting my `index.html` into a subdirectory it is creating called `_` (underscore), what's going on ? – kris Jul 28 '16 at 01:47
  • 2
    When you say "in your js file" what do you mean? What if I don't have a JS file? – evolutionxbox Oct 24 '16 at 08:53
  • 1
    absolutely. This one line in the entry script, i.e. `main.js` is importing everything within the `static` folder: `require.context("./static/", true, /^.*/);` – Mario Oct 30 '16 at 22:41
  • 2
    This is a neat hack but if you're copying too many files over you'll run out of memory. – Tom Feb 16 '17 at 20:00
  • When adding this to main.js - `Property 'context' does not exist on type 'NodeRequire'.` – fidev Oct 10 '17 at 09:44
  • 1
    Doesn't work anymore in Webpack 5 (_Error: Compiling RuleSet failed: Query arguments on 'loader' has been removed in favor of the 'options' property_). – kenorb Oct 04 '21 at 18:39
37

One advantage that the aforementioned copy-webpack-plugin brings that hasn't been explained before is that all the other methods mentioned here still bundle the resources into your bundle files (and require you to "require" or "import" them somewhere). If I just want to move some images around or some template partials, I don't want to clutter up my javascript bundle file with useless references to them, I just want the files emitted in the right place. I haven't found any other way to do this in webpack. Admittedly it's not what webpack originally was designed for, but it's definitely a current use case. (@BreakDS I hope this answers your question - it's only a benefit if you want it)

steev
  • 916
  • 10
  • 24
21

Webpack 5 adds Asset Modules which are essentially replacements for common file loaders. I've copied a relevant portion of the documentation below:

  • asset/resource emits a separate file and exports the URL. Previously achievable by using file-loader.
  • asset/inline exports a data URI of the asset. Previously achievable by using url-loader.
  • asset/source exports the source code of the asset. Previously achievable by using raw-loader.
  • asset automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.

To add one in you can make your config look like so:

// webpack.config.js

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
                type: "asset/resource"
            }
        ]
    }
};

To control how the files get output, you can use templated paths.

In the config you can set the global template here:

// webpack.config.js
module.exports = {
    ...
    output: {
        ...
        assetModuleFilename: '[path][name].[hash][ext][query]'
    }
}

To override for a specific set of assets, you can do this:

// webpack.config.js

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
                type: "asset/resource"
                generator: {
                    filename: '[path][name].[hash][ext][query]'
                }
            }
        ]
    }
};

The provided templating will result in filenames that look like build/images/img.151cfcfa1bd74779aadb.png. The hash can be useful for cache busting etc. You should modify to your needs.

David Archibald
  • 1,431
  • 14
  • 27
  • 3
    Also, although your advice is entirely consistent with the doco, no assets are being copied for me :-\ – David Bullock May 03 '21 at 07:43
  • 1
    @DavidBullock you may be a victim of tree shaking. If you're not importing the asset somewhere in a used file, e.g. `import myPath from "image.png";` and then using it, then Webpack won't copy it. Alternatively you may be using the Webpack dev server options which include an in memory file system and won't write to the filesystem. If none of that answers your question I'd suggest opening a question as there's limited information I can enumerate here. – David Archibald May 03 '21 at 18:42
  • 1
    Ah ha! An explicit `import` gets the job done. I won't be putting an `import` for each asset I want copied though! Since this is not fundamentally a dependencies/bundling/minification/transpiling step in the build, I'll do it outside of webpack, I think. – David Bullock May 04 '21 at 04:31
  • 2
    But for interest's sake, can one exclude certain rules from Tree Shaking? It would make sense to do so for Asset Modules, right? – David Bullock May 04 '21 at 04:35
  • @DavidBullock I'd go ahead and use [copy-webpack-plugin](https://webpack.js.org/plugins/copy-webpack-plugin/) if you merely want assets copied. However for a more complete solution I'd suggest integrating fully with Webpack if possible, e.g. CSS and HTML both have respective setups. You might also need to use [imports with expressions](https://webpack.js.org/guides/dependency-management/#require-with-expression). This way unused images can still be pruned but you don't have to manually make Webpack recognize the imports are being used. – David Archibald May 04 '21 at 07:24
  • Thank you for this answer! Was hoping I'd find the Webpack 5 approach here – mowwwalker Jun 03 '22 at 03:15
8

Above suggestions are good. But to try to answer your question directly I'd suggest using cpy-cli in a script defined in your package.json.

This example expects node to somewhere on your path. Install cpy-cli as a development dependency:

npm install --save-dev cpy-cli

Then create a couple of nodejs files. One to do the copy and the other to display a checkmark and message.

copy.js

#!/usr/bin/env node

var shelljs = require('shelljs');
var addCheckMark = require('./helpers/checkmark');
var path = require('path');

var cpy = path.join(__dirname, '../node_modules/cpy-cli/cli.js');

shelljs.exec(cpy + ' /static/* /build/', addCheckMark.bind(null, callback));

function callback() {
  process.stdout.write(' Copied /static/* to the /build/ directory\n\n');
}

checkmark.js

var chalk = require('chalk');

/**
 * Adds mark check symbol
 */
function addCheckMark(callback) {
  process.stdout.write(chalk.green(' ✓'));
  callback();
}

module.exports = addCheckMark;

Add the script in package.json. Assuming scripts are in <project-root>/scripts/

...
"scripts": {
  "copy": "node scripts/copy.js",
...

To run the sript:

npm run copy

RnR
  • 144
  • 1
  • 4
  • 3
    OP wanted to accomplish the file moving inside webpack, not using npm scripts? – William S Sep 07 '16 at 11:41
  • 1
    Even when OP wanted to solve this inside webpack, it is possible he is running webpack through npm, so he could add it to his build script where webpack is run – Piro Feb 16 '18 at 16:14
  • 1
    This actually makes more sense. Webpack doesn't have this builtin most likely because it is not meant as a replacement for gulp/make/etc. – jmathew Aug 09 '21 at 20:16
8

The way I load static images and fonts:

module: {
    rules: [
      ....

      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        /* Exclude fonts while working with images, e.g. .svg can be both image or font. */
        exclude: path.resolve(__dirname, '../src/assets/fonts'),
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'images/'
          }
        }]
      },
      {
        test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
        /* Exclude images while working with fonts, e.g. .svg can be both image or font. */
        exclude: path.resolve(__dirname, '../src/assets/images'),
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/'
          },
        }
    ]
}

Don't forget to install file-loader to have that working.

RegarBoy
  • 3,228
  • 1
  • 23
  • 44
  • How do you handle duplicate filenames? Or better yet, you know of any way to preserve original path in new output directory? – cantuket Jan 31 '19 at 21:33
  • You shouldn't have duplicate filename with the same extension name in your project. What's point for keeping duplicates though if their content is identical ? If not, then name them differently according to their content. Though why would you use webpack if you want to keep your things in the original path? If you want only JS translation then Babel should be enough. – RegarBoy Feb 02 '19 at 12:20
  • 1
    If you're implementing component based development _(one of the main principles of which is encapsulation and more specifically in this case [information hiding](https://en.wikipedia.org/wiki/Information_hiding))_, then none of what you mentioned is pertinent. i.e. When someone adds a new component to the program they shouldn't need to check if there's another image named `logo.png` nor should they have to create an obtuse and "hopefully" unique filename to avoid global collision. Same reason we use [CSS Modules](https://github.com/css-modules/css-modules). – cantuket Feb 02 '19 at 19:06
  • 1
    As to why I want images to maintain the original path and filename; debugging mostly, same reason you'd use sourcemaps, but also [SEO](https://yoast.com/image-seo/#name). Regardless, the answer to my question was actually very simple...`[path][name].[ext]` and there's plenty of flexibility provided to modify this for specific environment or use case...[file-loader](https://webpack.js.org/loaders/file-loader/#function) – cantuket Feb 02 '19 at 19:07
  • 1
    That being said we did implement a variation of your example so thank you for providing! – cantuket Feb 02 '19 at 19:11
  • Alright, then I misunderstood the point. So you wanted to prevent filename duplicates. I thought you were asking, how it is possible to keep duplicates. – RegarBoy Feb 04 '19 at 15:03
6

You can write bash in your package.json:

# package.json
{
  "name": ...,
  "version": ...,
  "scripts": {
    "build": "NODE_ENV=production npm run webpack && cp -v <this> <that> && echo ok",
    ...
  }
}
Victor Pudeyev
  • 4,296
  • 6
  • 41
  • 67
  • 1
    In Windows, just use xcopy instead of cp: `"build": "webpack && xcopy images dist\\images\\ /S /Y && xcopy css dist\\css\\ /S /Y"` – SebaGra Sep 28 '17 at 23:58
  • 9
    Right, so your solution is to have a different script for each OS? – Maciej Gurban Oct 05 '17 at 14:08
  • 1
    Yes, for me a script for each OS is acceptable (it's really unix/non-unix, since a script on linux will run on Darwin or another POSIX *nix) – Victor Pudeyev Oct 09 '17 at 21:19
  • And that Windows example won't work with PowerShell as the default shell either. – Julian Knight Jan 26 '19 at 21:42
  • Unlike CopyWebpackPlugin, this option keeps file dates. OS issue might be problematic for open source, but for smaller teams is easily managed with Windows bash or wrapping xcopy with cp.bat. – Alien Technology May 02 '19 at 15:31
  • Wrapping xcopy with cp.bat as per @AlienTechnology seems to be a reasonable solution, for cross-OS compatibility. – Victor Pudeyev May 02 '22 at 03:44
  • This is a webpack question... I can write bash to do most things... doesn't mean I should – Liam Jan 09 '23 at 14:44
5

Most likely you should use CopyWebpackPlugin which was mentioned in kevlened answer. Alternativly for some kind of files like .html or .json you can also use raw-loader or json-loader. Install it via npm install -D raw-loader and then what you only need to do is to add another loader to our webpack.config.js file.

Like:

{
    test: /\.html/,
    loader: 'raw'
}

Note: Restart the webpack-dev-server for any config changes to take effect.

And now you can require html files using relative paths, this makes it much easier to move folders around.

template: require('./nav.html')  
Andurit
  • 5,612
  • 14
  • 69
  • 121
2

I was stuck here too. copy-webpack-plugin worked for me.

However, 'copy-webpack-plugin' was not necessary in my case (i learned later).

webpack ignores root paths
example

<img src="/images/logo.png'>

Hence, to make this work without using 'copy-webpack-plugin' use '~' in paths

<img src="~images/logo.png'>

'~' tells webpack to consider 'images' as a module

note: you might have to add the parent directory of images directory in

resolve: {
    modules: [
        'parent-directory of images',
        'node_modules'
    ]
}

Visit https://vuejs-templates.github.io/webpack/static.html

techNik
  • 41
  • 4
  • The `~` operator you mention, is specific to Vue JS and not a feature of webpack. Tried it with Svelte, doesn't seem to do the trick. – Niket Pathak Jan 27 '21 at 18:58
2

The webpack config file (in webpack 2) allows you to export a promise chain, so long as the last step returns a webpack config object. See promise configuration docs. From there:

webpack now supports returning a Promise from the configuration file. This allows to do async processing in you configuration file.

You could create a simple recursive copy function that copies your file, and only after that triggers webpack. E.g.:

module.exports = function(){
    return copyTheFiles( inpath, outpath).then( result => {
        return { entry: "..." } // Etc etc
    } )
}
Mentor
  • 965
  • 9
  • 21
1

lets say all your static assets are in a folder "static" at the root level and you want copy them to the build folder maintaining the structure of subfolder, then in your entry file) just put

//index.js or index.jsx

require.context("!!file?name=[path][name].[ext]&context=./static!../static/", true, /^\.\/.*\.*/);
abhisekpaul
  • 497
  • 7
  • 5
0

In my case I used webpack for a wordpress plugin to compress js files, where the plugin files are already compressed and need to skip from the process.

optimization: {
    minimize: false,
},
externals: {
    "jquery": "jQuery",
},
entry: glob.sync('./js/plugin/**.js').reduce(function (obj, el) {
    obj[path.parse(el).name] = el;
    return obj
}, {}),
output: {
    path: path.resolve(__dirname, './js/dist/plugin'),
    filename: "[name].js",
    clean: true,
},

That used to copy the js file as it is to the build folder. Using any other methods like file-loader and copy-webpack create issues with that.

Hope it will help someone.

balakrishnan
  • 383
  • 4
  • 12