webpack: question mark after [ext] in config file - javascript

webpack.config.js, under rules:
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
what does ?[hash] do?

The name parameter under the options object indicates what your file name will be evaluated to. In the [hash] case more specifically, where to place the generated content hash for the file.
So, the following config:
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]?[hash]'
}
}
Would generate something like:
path/to/file.png?e43b20c069c4a01867c31e98cbce33c9
The purpose for that is ability to easily invalidate these files when new versions are available. By having an unique hash to each file version,
the browser will discard the old one when there's a new version
You can specify hash types (md5, sha256, etc) and other configurations, read more:
https://webpack.js.org/loaders/file-loader/

Someone can correct me if I'm wrong, but I believe it has to do with adding a query string to bypass browser caching so that when you recompile, the newest version of your resources (source code or other resources) is loaded instead of an old cached version. And the hash is just so that the query string is based on the content of your raw resource.

hash is basically calculated for a build.
hash returns the build hash. If any portion of the build changes, this changes as well. check here and here for more details

Related

Render multiple pages unrelated to the main app with Webpack and Mustache

I'm developing a Chrome Extension and I use Webpack to bundle it. I've got my compiled bundle, which is the main part of the app, but I also need an options page to describe the functionality. This options page has nothing to do with the bundle, it's just a static HTML file.
I must put a lot of things in that options page so I want to render that page with Mustache and define all content with JavaScript. For the most part, I've done that.
Here's my Webpack config (I've removed the parts regarding my app):
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
output: {
path: path.join(__dirname, 'extension/build/')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/options/index.html',
inject: false
})
],
module: {
rules: [
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render: require('./src/options/index.js')
}
}
]
}
}
and in my src/index.js, I have:
require('./options/index.html')
This will open the template and render it with the data in src/options/index.js.
There's a problem with that, however. I run Webpack with webpack --watch and changes to index.js (it holds the template data) do not trigger a rebuild. Also, I would need to go through a lot of trouble to create another static HTML file in the same manner.
It would be ideal if HtmlWebpackPlugin automatically used the template I require() in my entry point so that I don't need to explicitly set it. Also, it would be great if it automatically used a js in that same location to get the data. For example:
require('./options/index.html`)
Renders the template with data from ./options/index.html.js and then emits it. It would be even better if it emitted it to a custom folder specified in the Webpack config.
Is that possible? I couldn't find a plugin/loader that does that.
Edit: I was able to partly fix the rebuild problem by specifying the render option as a function:
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render () {
var file = './src/options/index.js'
delete require.cache[require.resolve(file)]
return require(file)
}
}
}
But it still doesn't work properly. The rebuild would only trigger after I make changes to index.html. This means that if I change index.js, I need to go and save index.html as well to trigger the build.

Webpack how to negative match resource query

How can I add a resource query to my Webpack configuration so that if a require statement contains a query string, Webpack skips the loader I've specified in the configuration file. Consider the following configuration:
test: /\.(jpe?g|png|svg|gif|ico|webp)$/,
resourceQuery: /(?!ni-ignore)/i,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
fallback: "file-loader",
publicPath: `/static/images/`,
outputPath: `/static/images/`,
name: "[name]-[hash].[ext]"
}
}
]
I want Webpack to skip the rule if a require statement contains ni-ignore query.
// should skip the rule
const image = require("./image.jpg?ni-ignore");
// should follow the rule
const image = require("./image.jpg");
Is there any way to add a negative resource query matcher?
Try this
resourceQuery: /^((?!ni-ignore).)*$/i
It seems that a part was missing in regex.
I know this is a bit old, but in case someone still needs this, resourceQuery now accepts negative rules in the form of:
resourceQuery: { not: [/raw/] },
See https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax for more details.

Rule.issuer in Webpack. What properties belong to it?

Webpack's Rule option presents two things (in a full, not shortcut syntax): resource and issuer.
In a Rule the properties test, include, exclude and resource are matched with the resource and the property issuer is matched with the issuer.
So, it is somewhat clear what properties are related to a resource:
{
resource: {
test: ...,
include: ...,
exclude: ...,
},
issuer: { ...boom?? }
}
But what properties are matched with the issuer? There is just nothing for issuer in their docs:
A Condition matched with the issuer. See details in Rule conditions.
And details do not explain issuer.
Why? They have created an option, but haven't decided on its properties yet?
They have created an option, but haven't decided on its properties yet?
The value of issuer is a Condition. The most common Condition is an object with test, include and/or exclude properties, which you have used for the resource. Everything you can use for resource, you can also use for issuer.
In fact, Rule.resource expects itself a Condition, excerpt from the docs:
Rule.resource
A Condition matched with the resource. You can either supply a Rule.resource option or use the shortcut options Rule.test, Rule.exclude, and Rule.include.
The only difference to the issuer is that there are shortcuts (Rule.test, Rule.exclude and Rule.include), because that's the majority of the use-cases. It roughly translates to:
resource: {
test: Rule.test,
exclude: Rule.exclude,
include: Rule.include,
}
And details do not explain issuer.
Clicking on See details in Rule conditions leads to a description, which even contains an example. Excerpt:
Rule Conditions
There are two input values for the conditions:
The resource: An absolute path to the file requested. It's already resolved according to the resolve rules.
The issuer: An absolute path to the file of the module which requested the resource. It's the location of the import.
Example: When we import "./style.css" from app.js, the resource is /path/to/style.css and the issuer is /path/to/app.js.
That is definitely an explanation, but maybe it isn't good enough, so I'll explain it in more details.
To illustrate the purpose of issuer I will use a contrived example, which could potentially be a use-case for it. Let's assume that you have some JavaScript code, which you would like to show to the user (the actual code) and at the same time you want to run that code in another part of the application. The code in question will be a simple greeting function.
greeter.js
export default function greet(name) {
console.log(`Hello ${name}!`);
}
If we want to show the source of greeter.js, we could read it from the file system, but because we'd like to run it in the browser, this is not an option. As we are using webpack, we can use the raw-loader to import the greeter.js file as a string instead of JavaScript. Assuming we have configured it, we can create a module that prints the source code.
printer.js
import greetSource from "./greeter";
export default function printSource() {
console.log(greetSource);
}
In our entry point we want to use the greeter and the printer at the same time.
index.js
import greet from "./greeter";
import printSource from "./printer";
greet("World");
printSource();
Now we have a problem, because we've configured raw-loader for greeter.js, therefore greet will be a string, not a function and that will cause a runtime error. What we want is to import greeter.js in index.js as a regular JavaScript file, but we want to import it as a string in printer.js. We could use an inline loader definition, but that would be rather tedious.
This is where issuer comes in. Whichever JavaScript file is imported from printer.js should be passed through the raw-loader, and from any other file we'd like to use babel-loader.
We will define two webpack rules. Both rules target only JavaScript files, so we test for the .js file ending, for every file that is imported, this it the resource. We would like to know which file imported it (where the import statement was), this is the issuer.
printer.js
// Resource: greeter.js, Issuer: printer.js
import greetSource from "./greeter";
index.js
// Resource: greeter.js, Issuer: index.js
import greet from "./greeter";
For the rules, it means that we want to exclude printer.js as an issuer from the babel-loader rule, and include only printer.js for the raw-loader rule.
module: {
rules: [
{
loader: "babel-loader",
resource: {
test: /\.js$/
},
issuer: {
exclude: /printer\.js$/
}
},
{
loader: "raw-loader",
resource: {
test: /\.js$/
},
issuer: {
include: /printer\.js$/
}
}
]
}
Note: It's not necessary to include the resource option for the raw-loader rule and if you leave it out, it would apply the raw-loader to everything that is being imported in printer.js, which may or may not be what you want (think of including CSS to style the output)
Bundling and running the above code will produce the following output:
Hello World!
export default function greet(name) {
console.log(`Hello ${name}!`);
}

Import Javascript files as a string?

I want to be able to simply do an import file from 'file.js and then have file be a string of the contents within file.js. I've toyed around with raw-loader but it doesn't give me the same contents (instead it loads it in a different format). Any suggestions?
it doesn't give me the same contents (instead it loads it in a different format)
That seems to mean that there are other loaders from your config applied to the file. You can enforce that only the loader in the import statement is used by prefixing it with a !:
import file from '!raw-loader!file.js'
From the docs:
It's possible to overwrite any loaders in the configuration by prefixing the entire rule with !.
In Webpack 5 it's possible to handle it without raw-loader.
It's enough to add a rule with type: asset/source (see the docs). Note that in this case, if you use babel loader or other JS loaders, the code will still be processed by them if not overridden manually.
A simplified code example:
module: {
rules: [
{
test: /(?<!boilerplate)\.js$/, // a negative look-behind regex to exclude your file
exclude: /node_modules/, // also can be handled here, adding a folder with file(s) to be excluded
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /boilerplate\.js$/,
type: 'asset/source'
},
]
}

Dynamically add version number to dest output files w/ grunt

I have a package.json file with our version number, such as:
{
name: "myproject"
version: "2.0"
}
My goal is to dynamically add the version number from the package.json file into the output files. For example, in the javascript I don't want to manually update the version number, but would like something similar to this to be generated after each grunt build:
/* My Project, v2.0 */
window.myProject = {
version: "2.0"
};
Is there an easy way to do this in my Gruntfile.js configuration?
I implemented: https://github.com/erickrdch/grunt-string-replace
In my source css/js files, I use the text {{ VERSION }} which gets replaced with the version number set in the package.json file. Below is the config I added to Gruntfile.js.
'string-replace': {
version: {
files: {
// the files I did string replacement on
},
options: {
replacements: [{
pattern: /{{ VERSION }}/g,
replacement: '<%= pkg.version %>'
}]
}
}
},
pkg: grunt.file.readJSON('package.json'),
I think that what you only want to do is to put some kind of trick for unable the page to use the cache files that maybe the browser have, and by now, the only way for that cross-browser is putting something on the href urls like "app.v2_2.js" or "app.js?ver=22". So I use this grunt npm package:
https://www.npmjs.org/package/grunt-cache-breaker
By default it only adds a parameter to your javascript and in almost the cases is the thing you need for not using cache, but you can configure even if you change the name of the file in other grunt process. This only change the HTML headers to what you desire.
After you install the grunt-cache-breaker, add this to your GruntFile:
// Append a timestamp to 'app.js', 'controllers.min.js' which are both located in 'index.html'
// resulting in the index the call of : href="~/app.js?rel=1415124174159"...
cachebreaker: {
dev: {
options: {
match: ['app.js', 'styles.css']
},
files: {
src: ['dist/index.html']
}
}
},
Then where you load the modules:
grunt.loadNpmTasks('grunt-cache-breaker');
Add on the task you want to:
grunt.registerTask('deploy', [
'clean:app',
'copy:views',
'copy:imgs',
'copy:css',
'uglify:app',
'cssmin:app',
'cachebreaker:dev'
]);
And finally run the grunt action on the console/command prompt
> grunt deploy
I would suggest using the banner feature in grunt-contrib-concat
this can be done as well with the banner option of https://github.com/gruntjs/grunt-contrib-uglify - which takes also care of the minifiaction of the javascript files.
filerev provides this option now. Use process to manipulate the filename that will be otherwise suffixed with md5 hash of the file content. You can use this to insert your version to every file you want.
Ref: https://github.com/yeoman/grunt-filerev
create something like package.json in the root of your project
it should read that or you can do something like
pkg: grunt.file.readJSON('package.json'),
in that you'll have a version declaration which would obviously correspond to <%= pkg.version %> so have that string in your json output and then run grunt.config.process to do the variable replacement
do something similar for the comment header

Categories

Resources