441

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?

3

13 Answers 13

729

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 [email protected], use [email protected]. Otherwise use latest.

Sign up to request clarification or add additional context in comments.

21 Comments

This is so much simpler when you want to copy an entire directory (ie. static html, and other boilerplate images)!
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.
@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.
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?
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
|
199

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.

14 Comments

"Of course you need to install the file-loader first to make this work." Link to the aforementioned "file-loader" here. And here is how to install and use it.
You still have the problem of HTML files and all references in them not being loaded.
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...
@KamilTomšík So your recommendation is that we should use a webpack plugin to avoid webpack plugins? (Just kidding. I got your point.)
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.
|
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 !!

7 Comments

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.
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 ?
When you say "in your js file" what do you mean? What if I don't have a JS file?
absolutely. This one line in the entry script, i.e. main.js is importing everything within the static folder: require.context("./static/", true, /^.*/);
This is a neat hack but if you're copying too many files over you'll run out of memory.
|
39

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)

Comments

22

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.

6 Comments

Also, although your advice is entirely consistent with the doco, no assets are being copied for me :-\
@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.
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.
But for interest's sake, can one exclude certain rules from Tree Shaking? It would make sense to do so for Asset Modules, right?
@DavidBullock I'd go ahead and use 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. This way unused images can still be pruned but you don't have to manually make Webpack recognize the imports are being used.
|
7

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

3 Comments

OP wanted to accomplish the file moving inside webpack, not using npm scripts?
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
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.
7

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.

6 Comments

How do you handle duplicate filenames? Or better yet, you know of any way to preserve original path in new output directory?
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.
If you're implementing component based development (one of the main principles of which is encapsulation and more specifically in this case 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.
As to why I want images to maintain the original path and filename; debugging mostly, same reason you'd use sourcemaps, but also SEO. 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
That being said we did implement a variation of your example so thank you for providing!
|
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", ... } } 

7 Comments

In Windows, just use xcopy instead of cp: "build": "webpack && xcopy images dist\\images\\ /S /Y && xcopy css dist\\css\\ /S /Y"
Right, so your solution is to have a different script for each OS?
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)
And that Windows example won't work with PowerShell as the default shell either.
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.
|
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') 

Comments

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

1 Comment

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.
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 } ) } 

Comments

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, /^\.\/.*\.*/); 

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.