I will do versioning via hashing. And this with Webpack.
Content of: webpack.config.js
...
output: {
path: path.resolve(__dirname, 'web/dist'),
filename: '[name].[contenthash].js',
},
...
webpack generate some files, i think its okay, its a hash...
app.d471b97fd69a6837f5bf.js
In my index.html file must replace the filenames to, i do it with "replacer-contenthash-webpack-plugin" Plugin for Webpack. It used the loaderUtils.getHashDigest(buffer) do generate a hash. But its not the same its generate this 174d0b0d04495cf7a970 hash.
Content of: index.html
<script src="dist/app.174d0b0d04495cf7a970.js"></script>
how can i fix it? I dont no how webpack generate the hash with [hashcode]. Its not specified.
i have tried, to specified contenthash digest, length and function:
output: {
path: path.resolve(__dirname, 'web/dist'),
filename: '[name].[contenthash].js',
hashFunction: "md5",
hashDigest: "hex",
hashDigestLength: 32
},
and specified the used Webpack extension getHashDigest() so:
getHashDigest(buffer, "md5", "hex", 32)
=>
app.54949ecc3046a8e7e56e8d9875970ca2.js
but index.html different :(
<script src="dist/app.174d0b0d04495cf7a970e84cc9a96cdb.js"></script>
The webpack plugin you are using (replacer-contenthash-webpack-plugin) is not very reputable, it's only getting around 200 downloads a week.
Check out the HtmlWebpackPlugin, it does the same thing and is getting around 13 million downloads a week.
The docs are straightforward and hopefully will sort your problem.
https://webpack.js.org/plugins/html-webpack-plugin
https://www.npmjs.com/package/html-webpack-plugin
Related
I've got a question. How does one inject the JS code from three different files using webpack?
I've managed to write a code like just below (webpack.config.js), but somehow it doesn't work. In the bundle.js it implements only first source (index.js) but other two are omitted.
Can anyone help me? I'm just a noobie in this and I'm still learning. Thanks a lot.
const path = require('path')
module.exports = {
mode: 'development',
entry: ['./index.js','./chat.js', './ui.js'],
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
watch: true
}
In such cases I prefer to have just one entry point that loading all dependencies, e.g. in index.js import other modules:
import('chat.js')
import('ui.js')
I have a file in my project called test.js
I don't import/require it anywhere which means my webpack won't call babel-loader for that js file.
Question: what I want is to move test.js into /dist folder, but as a compiled/transpiled. What's the best practice for it?
What I tried: I tried to use a copy-webpack-plugin and use its transform parameters before copying the file, but I can't seem to find the good babel package.
{
from: 'test.js',
to: '/dist/test.js',
transform(content, path) {
//what do I write here?
},
}
The simplest approach I could think about is to use several entry points like this:
{
entry: {
a: "./your-main-stuff",
b: "./your-single-file",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js"
}
}
which will create your a.js main bundle and b.js file in __dirname/dist folder both transpiled provided you used corresponding loader(s).
And from copy-webpack-plugin docs section:
webpack-copy-plugin is not designed to copy files generated from the
build process; rather, it is to copy files that already exist in the
source tree, as part of the build process.
so it seems to be difficult (if possible) making it move transpiled files.
Update. If you want to output files into different folders w/o changing your src folder, additonal tools needed. For your case (just 1 file) I would write a simple script and add it into package.json script section combined with webpack call like:
"scripts": {
"dev": "webpack && babel path-to-script.js --out-file path-to-script-compiled.js"
}
Just like in the previous answer, initially I went with the "scripts" entry in package.json that runs babel. But for a number of reasons I wanted to use webpack 5 to do the job. So after failing with webpack-copy-plugin and a good amount of digging around I came to this solution:
let config = {
entry: [
glob.sync(srcDir + '/**/*.js') // get all .js files from the source dir
],
output : {
filename : '[name].rem.js', // webpack wants to bundle - it can bundle here ;)
path: outDir
},
resolve: {
alias: {
'app': appDir
}
},
plugins: [
new RemoveEmptyScriptsPlugin({extensions: ['js'], scriptExtensions: /\.rem\.js/}) // for all .js source files that get bundled remove the bundle .rem.js file
],
module: {
rules:[{
test: /\.jsx?$/,
type: 'asset/resource', // get webpack to take it out instead of bundling
generator: {
filename: ({filename}) => filename // return full file name so directory structure is preserved
},
use: {
loader: 'babel-loader',
options: {
targets: { node: 16 },
presets: [
['#babel/preset-env', { modules: 'commonjs' /* transpile import/export */}],
]
}
}
}]
}
};
// Since the code does not go through the full pipeline and imports are not getting resolved, aliases will remain in the code.
// To resolve them it takes to hack the aliases object into the babel config
config.module.rules[0].use.options.plugins.push(['babel-plugin-webpack-alias-7', {config: {resolve: {alias: config.resolve.alias}}}];
And it does a good job but beware that it takes to use the patched versions of the two plugins (unless the patches have been merged already)!
There are a lot of questions on this, and a lot of answers, read below to see what I've tried, and how they didn't work. I assume my problem is coming from a fundamental misunderstanding of how url-loader works.
I have images included like this in my .less files. I am using two different formats as an example of what I've tried.
app.less
#logo {
background-image: url("~/img/LoginMarketingImage.png");
}
#logotwo{
background-image: url("../public/img/LoginMarketingImage2.png");
}
webpack.config.js
module.exports = {
context: path.resolve(__dirname, '../../'),
entry: {
app: './public/js/app.js'
},
output: {
path: path.resolve(__dirname, '../build'),
filename: '[name].js',
publicPath: '/'
},
I have also tried
webpack.config.js
module.exports = {
context: path.resolve(__dirname, '../../'),
entry: {
app: './public/js/app.js'
},
output: {
path: path.resolve(__dirname, '../build'),
filename: '[name].js',
publicPath: ''
},
As well as below, based on: webpack css-loader not finding images within url() reference in an external stylesheet
webpack.config.js
module.exports = {
context: path.resolve(__dirname, '../../'),
entry: {
app: './public/js/app.js'
},
output: {
path: path.resolve(__dirname, '../build'),
filename: '[name].js',
publicPath: 'https://localhost:9081/'
},
My module config looks like
webpack.config.js
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 2000000,
name: utils.assetsPath('img/[name].[ext]')
}
},
]
}
I have experimented with a limit of 1 and a higher limit, to see if inline vs non-inlined works.
My folder structure is as follows
build/
less/
app.less
public/
img/
LoginMarketingImage.png
LoginMarketingImage2.png
config/
webpack/
webpack.config.js
My Results
If I set the limit to 1, and force all images to be output directly, the only time they are output is if they use the url("../") syntax. url("~/") throw no webpack errors, but the images don't get output to the build folder. In this case, the images using url("../") syntax throw no errors in the browser either. However, this is not ideal, as I want to take advantage of url-loader's ability to return a DataURL. In this scenario, images using url("~/") syntax give the error GET https://localhost:9081/img/logo.png 404 (Not Found), regardless of publicPath setting.
If I set the limit to 20000000, obviously no images are put in the build folder by file-loader. However, all images return a 404 in the browser, regardless of publicPath setting.
I feel like I'm misunderstanding how to use url-loader. What kind of configuration do I need, and how should I be requiring my files inside less, to take advantage of url-loader's ability to return DataURLs?
EDIT: Based on this issue, I have ensured that css sourcemaps are disabled.
I can't pinpoint the problem here, but it seems like the publicPath config is the problem. I threw together a sample gist with the minimium you need to get the url-loader working properly.
Some things to note:
Make sure the test option is correct
You didn't mentioned which version of webpack you're using, but I assume it's > 2. If it's == 2 then you should upgrade because the newer versions fixed a lot of bugs and the config API is almost 100% compatible
Do not use the tilde import path, it's a shortcut to project-root-dir/node_modules (at least in webpack 1.x)
I assume you're using webpack-dev-server. If not, then you should not need to set the publicPath and such
Do not change url-loader's name option unless you got everything working without it. Changing it's path can confuse webpack
I have a project that makes use of a large bundle (dexie.js not that it's important to this question), that I'd like to 'split out' into it's own bundle that I can include manually, because it's only needed in a few of my entry point scripts.
Just so that you know: my webpack config uses multiple modules, with multiple (6) entry points, so this is a cut-down sample of my webpack.config.js:
{
context: path.join(__dirname, 'src'),
entry: {
'js/contentscript.js' : './js/contentscript.js',
'js/background.js' : './js/background.js',
'js/popup.js' : './js/popup.js',
'js/options.js' : './js/options.js',
},
output: {
path: path.join(__dirname, 'dist'),
filename: "[name]"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "dexie",
filename: "js/dexie.js",
minChunks: function (module) {
// this assumes your vendor imports exist in the node_modules directory
return module.context && module.context.includes("node_modules/dexie");
}
}),
... // other non relevant plugins here
]
}
The problem is that I have no typical 'vendor' nor 'common' requirement, like many other projects to. It's only in a few cases that I want to include the js/dexie.js bundle.
eg. in options.html, I have this:
<script src="js/dexie.js"></script>
<script src="js/options.js"></script>
...but I do not want it to be used for popup.html, which I want to keep as this:
<script src="js/popup.js"></script>
And since this is also a WebExtensions project, I definitely don't want my content scripts to need it!
The problem is, that when I open popup.js, I get the error: Uncaught ReferenceError: webpackJsonp is not defined.
Is there any way -- without splitting this into more webpack modules -- so that the CommonsChunkPlugin will play nice with the entry points so that only the ones I want are affected by it?
The only solution I can think of, is to make another CommonsChunkPlugin that acts in the way that 'vendor' and 'common' is typically used. That is:
new webpack.optimize.CommonsChunkPlugin({
name: "dexie",
filename: "js/dexie.js",
minChunks: function (module) {
// this assumes your vendor imports exist in the node_modules directory
return module.context && module.context.includes("node_modules/dexie");
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
filename: "js/manifest.js",
minChunks: Infinity
}),
Unfortunately, this means I need to include js/manifest.js in all my scripts, and since this is a WebExtension, then that means I have to inject it into each content page...IMHO this is a terrible solution.
Any ideas on how to improve this? Am I using CommonsChunkPlugin incorrectly? Is there a better module I should be using? (I'm actually still coming to grips with webpack!)
First of all, using 'js/xxx.js' as an entry name is not a good way.
If your options.js need dexie.js and popup.js do not need it.
You can try to change files like below.
webpack.config.js
entry: {
'vendor' : ['dexie'],
'contentscript' : './js/contentscript.js',
'background' : './js/background.js',
'popup' : './js/popup.js',
'options' : './js/options.js',
},
resolve: {
alias: {
'dexie':'...'
}
},
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
}),
Like I said before, CommonChunksPlugin will automatically help u extract common dependencies, in this case you dont need write a function of minChunks to indicate dependencies.
options.html
<script src="js/vendor.js"></script>
<script src="js/options.js"></script>
popup.html
<script src="js/popup.js"></script>
I stumbled across this excellent answer by #prograhammer: https://stackoverflow.com/a/40416826/125525
In it he made reference to the Externals plugin, which I'd not heard of before, and it neatly solves my problems. This is the description from the webpack site:
For example, to include jQuery from a CDN instead of bundling it:
index.html
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous">
</script>
webpack.config.js
externals: {
jquery: 'jQuery'
}
This leaves any dependent modules unchanged, i.e. the code shown below
will still work:
import $ from 'jquery';
$('.my-element').animate(...);
Im using a file-loader to automatically render a bunch of pug templates to static html files, but webpack is also outputting a pointless file based on the entry point
Eg this is in my webpack.config.js:
entry: {
'js/build/bundle.js': './js/app.js',
'this-shouldnt-emit': './pug/.pug.js' //pug entry point
},
output: {
path: path.join(__dirname, '../'),
filename: '[name]'
},
...
// pug loading module rule
{
test: /\.pug$/,
include: path.resolve(__dirname, "../pug"),
use: [
"file-loader?name=[path][name].html&context=./pug",
"pug-html-loader?pretty&exports=false"
]
}
and Im getting a this-shouldnt-emit bundle file in the root build directory, which I dont want.
How can I stop the file-loader from emitting an output bundle, but not interfere with it generating all the static html files it currently is. Is there a plugin or some kind of null loader I can put at the end of the loader chain to kill the bundle emitting?
I am not sure of what you are really asking but if you want it include multiple files in one bundle, use an array. The whole point of using [name] in filename: '[name].bundle.js is for multiple entries. It is not necessary (but optional) for one entry key.
This will create two bundle files.
entry: {
BUNDLE_KEY_ONE: 'path/file.js',
BUNDLE_KEY_TWO: 'path/otherFile.js',
}
This is how you have multiple files one bundle.
entry: {
SINGLE_BUNDLE_NAME: ['file/one.js', 'file/two.js']
}