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?
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.
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.
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 !!
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)
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.
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
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.
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",
...
}
}
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')
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
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
} )
}
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, /^\.\/.*\.*/);
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.
Related
I have an existing node js backend that need to deploy on customer premise. I would like to compile/obfuscate the code before passing to client.
I do not know whether it is "compilable".
But at very least i want to have only single merged code into index.js file (other javascript file all remove) that is obfuscated before passing to client.
Is there any existing npm module that does that and how reliable that an obfuscated code work as it is then original code.
What are your company/ ways to deal with it when u need to pass an existing node js source code deployable at client premise.
I am much more looking at existing library that can automate the whole process.
eg: npx run obfuscate-code --entrypoint.js (it will search all the import/require from node js and compile everything.
Folder structure is as follow.
home
- controllers (folder)
- file1.js
- file2.js
- languages (folder)
- en.js
- services (folder)
- service1.js
- service2.js
- index.js (entry point)
You can make an executable using PKG (npm package) see => https://github.com/zeit/pkg
or webpack if you want to make a single js file that is minimized.
// webpack.config.js
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'production',
target: 'node',
externals: [nodeExternals()],
entry: {
'build/output': './src/index.js'
},
output: {
path: __dirname,
filename: '[name].bundle.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['env', {
'targets': {
'node': 'current'
}
}]
]
}
}
}]
}
};
By using nodeExternals you don't put external dependencies in the main js file but you refer to node_modules.
You can use both those 2 solutions using npm run ... by adding them to your scripts section in package.json
I have a vue.js (version 2.4.4) application built with webpack (version 3.6.0) and vue-loader (version 13.0.5).
In the .vue files, I need to modify the url contained in the src attribute of the <img> tags according to the environment of my application.
In the development environment, the images must come from the
application folder, with a relative path: "/src/images/exemple.png"
In the production environment, the images must come from a cdn, with
an absolute path: "https://my-cdn.net/images/exemple.png"
In the "webpack.config.js" file, I already differentiate the different environments using "process.env.NODE_ENV", like this:
const isDev = process.env.NODE_ENV === 'dev';
But I don't know how to modify the src attribute of the img tags in my .vue files with vue-loader (or with something else).
For information, here is the definition of the vue-loader in the "webpack.config.js" file:
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
'scss': [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
}
}
Is there a simple way to do this?
Piggybacking off of #Michael Levy's answer:
I am currently having this issue with Vue 3, #vue/cli 4.5.10, and webpack. I've solved it after much research.
Webpack configurations go into vue.config.js, where there is a lot of abstraction. To fine tune control, you can use chain webpack configs. To help you, use Vue Inspect when you are trying to access specific loaders via chaining.
$ vue inspect > output.js
That will give you a nice list of all the loaders that vue-cli is using.
For example - to modify webpack image options within vue.config.js, you can use vue inspect > output.js, search the output.js file, and discover the loader that's managing images.
Which is: /* config.module.rule('images').use('url-loader') */
To answer the question - in your vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule("images")
.use("url-loader")
.tap((options) => {
options.name = "images/[name].[ext]";
options.publicPath = isDev ? __webpack_public_path__ : 'https://my-cdn.net/';
return options;
});
},
};
Vue-loader is preconfigured to handle src attributes in Vue single file components as you can see here. So for example <img src="../image.png"> in the template is transformed into:
createElement('img', {
attrs: {
src: require('../image.png') // this is now a module request
}
})
What Webpack do with this require depends on configured loaders. Usual there is a file-loader configured. It looks like this (from project generated by Vue CLI + simplified):
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
]
}
]
}
Loader is responsible for copying your file into dist directory and returning public URI, which will be inserted into src attribute.
So what you want can be configured here, by specifying right options. For example:
options: {
name: 'images/[name].[ext]'
publicPath: isDev ? __webpack_public_path__ : 'https://my-cdn.net/'
}
Just take content of dist/images directory after the build and deploy it so it is accessible by https://my-cdn.net/images and it should work....
You can create an alias for/src/images and alter the url at transpile time based on the environment:
{
//..
resolve: {
alias: {
'/src/images': isDev ? '/src/images' : process.env.IMAGE_CDN + '/images'
}
}
}
Another way to handle this would be to use DefinePlugin to create a global variable that each of your images reference.
module.exports = {
chainWebpack: config => {
console.log('\n')
console.log('Setting global variables:')
console.log(`__YOUR_GLOBAL_CONSTANT__: ${JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)}`)
console.log('\n')
config
.plugin('provide')
.use(require('webpack').DefinePlugin, [{
__YOUR_GLOBAL_CONSTANT__: JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)
}])
}
}
The example above is utilizing a vue.config.js file, but the strategy should be pretty similar. Also, if you're using something like eslint, you'll need to specify the variable in the globals section as readonly.
Although my dev server is running on localhost:3000, I have set up my host file to point www.mysite.com to localhost. In my JavaScript, I have code like:
import myImage from '../assets/my-image.jpg'
const MyCmp => <img src={myImage} />
Using Webpack's file-loader, it transforms that import into a URL to the hosted image. However, it uses the localhost path to that image, but I'd like it to use the www.mysite.com domain. I looked at both the publicPath and postTransformPublicPath options for file-loader, but those only appear to allow you to modify the part of the path that comes after the domain.
I personally don't like the notion of defining host-information statically in the build output. This is something that should be determined in runtime based on where you actually put your files.
If you are like me then there are two options here.
Both involve you calling a global method that you have defined on i.e. window / global scope.
The purpose of the global method is to resolve the root path (the domain, etc) in runtime.
Define a global method
So lets say you define a method on the global scope somewhere in your startup code like so:
(<any>window).getWebpackBundleRootPath = function (webpackLibraryId) {
if (webpackLibraryId == null) return throw "OOOPS DO SOMETHING HERE!";
// Preferably these variables should be loaded from a config-file of sorts.
if(webpackLibraryId == "yourwebpacklibrary1") return "https://www.yoursite.com/";
// If you have other libraries that are hosted somewhere else, put them here...
return "...some default path for all other libraries...";
};
The next step is to configure webpack to call this global method when it tries to resolve the path.
As I mentioned there are two ways, one that manipulates the output of the webpack and one that is more integrated in webpacks configuration (although only for file-loader but I think it should suffice).
It's worth mentioning that you don't need a global method if you only have one bundle or if you host all your bundles in one place. Then it would be enough to use a global variable instead. It should be quite easy to modify the example below to accommodate this.
First option: configure webpack file-loader to call your method when resolving path
This solution will not require something to be done post build. If this fits your need and covers all scenarios I would go for this option.
Edit your webpack config file
var path = require('path');
let config = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
module: {
rules: [{
// Please note that this only defines the resolve behavior for ttf. If you want to resolve other files you need to configure the postTransformPublicPath for those too. This is a big caveat in my opinion and might be a reason for using second option.
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
outputPath: 'assets/fonts', // The folder where you store your fonts.
name: '[name].[ext]',
// This is where the magic happens. This lets you override the output of webpack file resolves.
postTransformPublicPath: function (p) {
// Because of the way webpack file-loader works the input from to this method will look something like this: __webpack_public_path__ + "/assets/fonts/yourfont.ttf"
// But we are not interested in using the __webpack_public_path__ variable so lets remove that.
p = p.replace('__webpack_public_path__ + ', '');
// Return a stringified call to our global method and append the relative path to the root path returned.
return `getWebpackBundleRootPath("${config.output.library}") + ${p}`;
}
}
}]
},
},
...
};
module.exports = config;
As you might have noticed in the comments in the webpack config file you need to specify the resolve behavior for each file-loader that you add (if someone knows a better way, please let me know). This is why I still use the second option.
Second option: manipulate the output of the webpack in a postbuild step
Example webpack.config.js file
For completeness sake here is an example of a webpack.config.js file that contains the variables used in the postbuild script.
var path = require('path');
module.exports = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
...
}
Create a postbuild.js file
Create a file postbuild.js next to your package.json with the following content:
const fs = require('fs');
// We take the path to the webpack config file as input so that we can read settings from it.
const webpackConfigFile = process.argv[2];
// Read the webpack config file into memory.
const config = require(webpackConfigFile);
// The file to manipulate is the output javascript bundle that webpack produces.
const inputFile = config.output.path + config.output.filename;
// Load the file into memory.
let fileContent = fs.readFileSync(inputFile, 'utf8');
// Replace the default public path with the call to the method. Please note that if you specify a publicPath as '/' or something very common you might end up with a problem so make sure it is unique in the file to avoid other unrelated stuff being replaced as well.
fileContent = fileContent.replace('"' + config.output.publicPath + '"', 'getWebpackBundleRootPath("' + config.output.library + '")');
// Save the manipulated file back to disk.
fs.writeFileSync(inputFile, fileContent, 'utf8');
Call the postbuild.js automatically on build
Next step is to actually call the postbuild.js script after each build.
This can be done in a postscript in package.json like so (in the script section in your package.json):
{
"scripts": {
"build": "webpack",
"postbuild": "node postbuild.js ./webpack.config.js"
}
}
From now on whenever you run the build script it will also run the postbuild script (from npm or yarn, etc).
You can of course also manually run the postbuild.js script manually after each build instead.
but those only appear to allow you to modify the part of the path that comes after the domain.
Not really, you can give it an URL that includes the domain.
In your case, assuming your images are under the assets directory, you will have something like this in your webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpe?g|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
publicPath: 'https://www.example.com/assets',
outputPath: 'assets'
}
}
},
...
]
}
...
I'm learning React and want to understand how web pack is configured for a project.
It would be great if someone can tell me what the following lines of code are doing.
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
function isDirectory(dir) {
return fs.lstatSync(dir).isDirectory()
}
const SubjectsDir = path.join(__dirname, 'subjects')
const SubjectDirs = fs.readdirSync(SubjectsDir).filter(function (dir) {
return isDirectory(path.join(SubjectsDir, dir))
})
module.exports = {
devtool: 'source-map',
entry: SubjectDirs.reduce(function (entries, dir) {
if (fs.existsSync(path.join(SubjectsDir, dir, 'exercise.js')))
entries[dir + '-exercise'] = path.join(SubjectsDir, dir, 'exercise.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'solution.js')))
entries[dir + '-solution'] = path.join(SubjectsDir, dir, 'solution.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'lecture.js')))
entries[dir + '-lecture'] = path.join(SubjectsDir, dir, 'lecture.js')
return entries
}, {
shared: [ 'react', 'react-dom' ]
}),
output: {
path: '__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '__build__'
},
resolve: {
extensions: [ '', '.js', '.css' ]
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
{ test: /\.js$/, exclude: /node_modules|mocha-browser\.js/, loader: 'babel' },
{ test: /\.woff(2)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
{ test: /\.ttf$/, loader: 'file' },
{ test: /\.eot$/, loader: 'file' },
{ test: /\.svg$/, loader: 'file' },
{ test: require.resolve('jquery'), loader: 'expose?jQuery' }
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({ name: 'shared' })
],
devServer: {
quiet: false,
noInfo: false,
historyApiFallback: {
rewrites: [
{ from: /ReduxDataFlow\/exercise.html/,
to: 'ReduxDataFlow\/exercise.html'
}
]
},
stats: {
// Config for minimal console.log mess.
assets: true,
colors: true,
version: true,
hash: true,
timings: true,
chunks: false,
chunkModules: false
}
}
}
This informaiton is coming from a training course, but they do not explain what the lines are doing.
Webpack is what we call a module bundler for JavaScript applications. You can do a whole slew of things with it that help a client browser download and run your code. In the case of React, it helps convert JSX code into plain JS so that the browser can understand it. JSX itself will not run in the browser. We can even use plugins to help minify code, inject HTML, bundle various groups of code together, etc. Now that the introduction to Webpack is out of the way, let's take a look at the code. I will be starting from the very top. Feel free to skip down to #3 if you are only interested in the Webpack configuration object.
The following code will require the modules that are needed in this file. fs is short for "filesystem" and is a module that gives you functions to run that can access the project's filesystem. path is a common module used to resolve or create pathnames to files and is very easy to use! And then we have the webpack module through which we can access webpack specific functions (ie: webpack plugins like webpack.optimize.UglifyJsPlugin).
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
These next few lines first set up a helper function to determine whether or not something in the filesystem being read is a directory.
function isDirectory(dir) {
return fs.lstatSync(dir).isDirectory()
}
const SubjectsDir = path.join(__dirname, 'subjects')
const SubjectDirs = fs.readdirSync(SubjectsDir).filter(function (dir) {
return isDirectory(path.join(SubjectsDir, dir))
})
devtool specifies which developer tool you want to use to help with debugging. Options are listed here : https://webpack.github.io/docs/configuration.html#devtool. This can be very useful in helping you determine exactly which files and lines and columns errors are coming from.
devtool: 'source-map'
These next few lines tell Webpack where to begin bundling your files. These initial files are called entry points. The entry property in a Webpack configuration object should be an object whose keys determine the name of a bundle and values point to a relative path to the entry file or the name of a node_module. You can also pass in an array of files to each entry point. This will cause each of those files to be bundled together into one file under the name specified by the key - ie: react and react-dom will each be parsed and have their outputs bundled under the name shared.
entry: SubjectDirs.reduce(function (entries, dir) {
if (fs.existsSync(path.join(SubjectsDir, dir, 'exercise.js')))
entries[dir + '-exercise'] = path.join(SubjectsDir, dir, 'exercise.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'solution.js')))
entries[dir + '-solution'] = path.join(SubjectsDir, dir, 'solution.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'lecture.js')))
entries[dir + '-lecture'] = path.join(SubjectsDir, dir, 'lecture.js')
return entries
}, {
shared: [ 'react', 'react-dom' ]
}),
In the reduce function we simply read through the SubjectsDir, determine whether files exercise.js, lecture.js & solution.js exist, and then provide the path to those files as values associated with the key names identified by dir + '-' + filename (ie: myDir-exercise). This may end up looking like the following if only exercise.js exists:
entry : {
'myDir-exercise': 'subjectDir/myDir/exercise.js',
share: ['react', 'react-dom']
}
After we provide entry points to the Webpack configuration object, we must specify where we want Webpack to output the result of bundling those files. This can be specified in the output property.
output: {
path: '__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '__build__'
},
The path property defines the absolute path to the output directory. In this case we call it __build__.
The filename property defines the output name of each entry point file. Webpack understands that by specifying '[name]' you are referring to the key you assigned to each entry point in the entry property (ie: shared or myDir-exercise).
The chunkFilename property is similar to the filename property but for non-entry chunk files which can be specified by the CommonChunksPlugin (see below). The use of [id] is similar to the use of [name].
The publicPath property defines the public URL to where your files are located, as in the URL from which to access your files through a browser.
The resolve property tells Webpack what how to resolve your files if it can not find them for some reason. There are several properties we can pass here with extensions being one of them. The extensions property tells Webpack which file extensions to try on a file if one is not specified in your code.
resolve: {
extensions: [ '', '.js', '.css' ]
},
For example, let's say we have the following code
const exercise = require('./exercise');
We can leave out the .js because we have provided that string in the resolve property of the webpack configuration and Webpack will try and append .js to this at bundling time to find your file. As of Webpack 2 we also no longer need to specify an empty string as the first element of the resolve property.
The module property tells Webpack how modules within our project will be treated. There are several properties we can add here and I suggest taking a look at the documentation for more details. loaders is a common property to use and with that we can tell Webpack how to parse particular file types within our project. The test property in each loader is simply a Regex that tells Webpack which files to run this loader on. This /\.js$/ for example will run the specified loader on files that end with .js. babel-loader is a widely used JavaScript + ES6 loader. The exclude property tells Webpack which files to not run with the specified loader. The loader property is the name of the loader. As of Webpack 2 we are no longer able to drop the -loader from the string as we see here.
Plugins have a wide range of functions. As mentioned earlier we can use plugins to help minify code or to build chunks that are used across our entire application, like react and react-dom. Here we see the CommonChunksPlugin being used which will bundle the files under the entry name shared together as a chunk so that they can be separated from the rest of the application.
Finally we have the devServer property which specifies certain configurations for the behavior of webpack-dev-server, which is a separate module from webpack. This module can be useful for development in that you can opt out of building your own web server and allow webpack-dev-server to serve your static files. It also does not write the outputs to your filesystem and serves the bundle from a location in memory at the path specified by the publicPath property in the output property of the Webpack configuration object (see #5). This helps make development faster. To use it you would simply run webpack-dev-server in place of webpack. Take a look at the documentation online for more details.
The configuration object that we've taken a look at follows the Webpack 1 standard. There are many changes between Webpack 1 and 2 in terms of configuration syntax that would be important to take a look at. The concepts are still the same, however. Take a look at the documentation for information on migrating to Webpack 2 and for further Webpack 2 details.
So I have a SCSS file called style.scss. And I have an ES6 file called script.js. Both these files are in a ./src/ directory.
I want to use Webpack to take the style.scss, convert it to CSS and stick it into a folder called ./dist/ with the filename style.css. I can actually make that happen.
I also want Webpack to take the script.js, transpile to ES5, and put that in to the same ./dist/ folder with a file called script.js. I can also make that happen.
What I cannot do, is make both happen simultaneously. Is this something that Webpack can even do? In this instance I don't want all my styles putting into my JS, I specifically want them to be kept in separate files like a standard website (as that's what this is, really).
The closest I have got is this:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
script: './src/script.js',
style: './src/style.scss'
},
output: {
path: './dist',
filename: '[name].js'
},
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin('style.css')
] }
But this gives me a script.js file, a style.css file and ALSO a style.js file which is a mess.
Any suggestions?
You don't want to create a separate entry point for the CSS. You can either import the style.scss in your script.js or you can add it to the same entry. An entry can also be an array and webpack will include them in the same bundle, well the CSS is extracted anyway so there won't really be any additional JavaScript. Your entry would be as follows:
entry: {
script: ['./src/script.js', './src/style.scss']
},