Webpack different names for entries - javascript

How i can specify different filename for different entry output?
For example:
module.exports = {
context: path.resolve(__dirname, 'assets'),
entry: {
vendor: ['react', 'react-dom', 'lodash', 'redux'],
app: './src/app.js'
}
output: {
path: path.resolve(__dirname, (isDevelopment) ? 'demo' : 'build'),
filename: (isDevelopment) ? '[name].js' : '[name][chunkhash:12].js'
}
}
To receive output like this
build
-- index.html
-- app.2394035ufas0ue34.js
-- vendor.js
So browser will cache vendor.js with all libraries. Since i don't plan to migrate to any major new release anytime soon and often.
And still being able to break cache for app.js with every update required.
is there some kind of option to set output as
output: {
app: {
...
},
vendor: {
...
},
}

Here is working code:
entry: {
'./build/app': './src/app.js',
'./build/vendor': VENDOR_LIBS // or path to your vendor.js
},
output: {
path: __dirname,
filename: '[name].[chunkhash].js'
},
Add this code into your webpack plugins array as last element of an array.
plugins: [
... // place our new plugin here
]
function() {
this.plugin("done", function(stats) {
const buildDir = __dirname + '/build/';
const fs = require('fs');
var vendorTempFileName = '';
new Promise(function(resolve, reject) {
fs.readdir(buildDir, (err, files) => {
files.forEach(file => {
if (file.substr(0,6) === 'vendor') {
resolve(file);
}
});
});
}).then(function(file) {
fs.rename( buildDir + file, buildDir + 'vendor.js', function(err) {
if ( err ) console.log('ERROR: ' + err);
});
});
});
}
Output should be as follows:
It is considered bad practice to leave your files without chunkhashes, due to browser caching.

For Webpack 4 I added a quick-and-dirty done hook to rename my service worker script:
// Plugin to rename sw-[chunkhash].js back to sw.js
class SwNamePlugin {
apply(compiler) {
compiler.hooks.done.tap("SW Name Plugin", (stats) => {
const swChunk = stats.compilation.chunks.find((c) => c.name === "sw");
fs.rename(path.resolve(outDir, swChunk.files[0]), `${outDir}/sw.js`);
});
}
}
plugins.push(new SwNamePlugin());
This obviates the warning DeprecationWarning: Tapable.plugin is deprecated. Use new API on .hooks instead you'd see following loelsonk's answer.

Related

after upgrade to Webpack 5 not able to access the menifest in copy-webpack-plugin

My config was working fine in webpack version 4.6.0 and webpack-assets-manifest version 3.1.1
since I upgraded to webpack 5 and webpack-assets-manifest to 5. I'm not getting value in my manifestObject it's just empty object
I suspect this is happening because transform function running before manifest is created
looked into the new documentation of webpack-assets-manifest but could not get it working
my goal is to access manifest value in transform function, but it looks like transform function is running before manifest is generated
var CopyWebpackPlugin = require('copy-webpack-plugin');
var SaveHashes = require('webpack-assets-manifest');
const manifest = new SaveHashes({
entrypoints: true,
entrypointsKey: 'entryPoints'
});
module.exports = {
entry: {
main: ['./src/apps/main'],
games: ['./src/apps/games'],
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: assetsUrl,
filename: 'assets/javascript/[name].[contenthash].js',
chunkFilename: 'assets/javascript/[name].[contenthash].js'
},
.
.
.
.
.
plugins: [
new CleanWebpackPlugin(),
manifest,
new CopyWebpackPlugin([
{
from: './views/**/*',
to: path.join(__dirname, 'dist'),
transform(content, path) {
// I want to access manifest here
// so that I can inject the final script(javascript bundle with content hash)
// in my view template
const manifestObject = JSON.parse(manifest);
}
}
])
]
};
you need to export it like this
const ManifestPlugin = require("webpack-manifest-plugin").WebpackManifestPlugin;
.....
new ManifestPlugin({
fileName: "asset-manifest.json",
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[ file.name ] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
(fileName) => !fileName.endsWith(".map")
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
and it will work WebpackManifestPlugin is what you need

Webpack problem generating sourcemaps -- only generated when a particular plugin is disabled

I'm using my own custom plugin to strip some optional code from a build. This works well, but for some reason it seems to be blocking generation of source maps.
My best guess is that the fact that I'm modifying the index.js output file interferes with the ability to generate a map of for that file. If I comment out the plugin, my source maps come back.
Is there perhaps something I can do to change order of plugin execution that will fix this? Or perhaps a way to strip code from source file input streams (not from the files themselves) rather than from the generated output?
I've tried explicitly adding SourceMapDevToolPlugin to my plugins, but that didn't help.
Here's my webpack.config.cjs file:
const { Compilation, sources } = require('webpack');
const { resolve } = require('path');
module.exports = env => {
const esVersion = env?.esver === '5' ? 'es5' : 'es6';
const dir = env?.esver === '5' ? 'web5' : 'web';
const chromeVersion = env?.esver === '5' ? '23' : '51';
// noinspection JSUnresolvedVariable,JSUnresolvedFunction,JSUnresolvedFunction
return {
mode: env?.dev ? 'development' : 'production',
target: [esVersion, 'web'],
entry: {
index: './dist/index.js'
},
output: {
path: resolve(__dirname, 'dist/' + dir),
filename: `index.js`,
libraryTarget: 'umd',
library: 'tbTime'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: { presets: [['#babel/preset-env', { targets: { chrome: chromeVersion } }]] }
},
resolve: { fullySpecified: false }
}
]
},
resolve: {
mainFields: ['esm2015', 'es2015', 'module', 'main', 'browser']
},
externals: { 'by-request': 'by-request' },
devtool: 'source-map',
plugins: [
new class OutputMonitor {
// noinspection JSUnusedGlobalSymbols
apply(compiler) {
compiler.hooks.thisCompilation.tap('OutputMonitor', (compilation) => {
compilation.hooks.processAssets.tap(
{ name: 'OutputMonitor', stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE },
() => {
const file = compilation.getAsset('index.js');
let contents = file.source.source();
// Strip out dynamic import() so it doesn't generate warnings.
contents = contents.replace(/return import\(.*?\/\* webpackIgnore: true \*\/.*?tseuqer-yb.*?\.join\(''\)\)/s, 'return null');
// Strip out large and large-alt timezone definitions from this build.
contents = contents.replace(/\/\* trim-file-start \*\/.*?\/\* trim-file-end \*\//sg, 'null');
compilation.updateAsset('index.js', new sources.RawSource(contents));
}
);
});
}
}()
]
};
};
Full project source can be found here: https://github.com/kshetline/tubular_time/tree/development
I think using RawSource would disable the source map. The right one for devtool is supposed to be SourceMapSource so the idea looks like following:
const file = compilation.getAsset('index.js');
const {devtool} = compiler.options;
let contents = file.source.source();
const {map} = file.source.sourceAndMap();
// your replace work
// ...
compilation.updateAsset(
'index.js',
devtool
// for devtool we have to pass map file but this the original one
// it would be wrong since you have already changed the content
? new sources.SourceMapSource(contents, 'index.js', map)
: new sources.RawSource(contents)
);

ERROR in Entry Module not found: Error: Can't resolve '*source*'

I am following a course on Udemy which is about Wordpress development. Following the course I came up with this problem, I was trying to add google maps to a custom post type, for that I needed to update javascript file. But whenever I am running 'gulp scripts', this error occurs. I have no idea about node, gulp, webpack. I am just following the course. I looked up on the internet for a long time on this issue but found nothing.
I am using XAMPP. And before this, 'gulp watch' was working fine and php scripts were getting updated just fine.
gulpfile.js:-
var gulp = require('gulp'),
settings = require('./settings'),
webpack = require('webpack'),
browserSync = require('browser-sync').create(),
postcss = require('gulp-postcss'),
rgba = require('postcss-hexrgba'),
autoprefixer = require('autoprefixer'),
cssvars = require('postcss-simple-vars'),
nested = require('postcss-nested'),
cssImport = require('postcss-import'),
mixins = require('postcss-mixins'),
colorFunctions = require('postcss-color-function');
gulp.task('styles', function() {
return gulp.src(settings.themeLocation + 'css/style.css')
.pipe(postcss([cssImport, mixins, cssvars, nested, rgba, colorFunctions, autoprefixer]))
.on('error', (error) => console.log(error.toString()))
.pipe(gulp.dest(settings.themeLocation));
});
gulp.task('scripts', function(callback) {
webpack(require('./webpack.config.js'), function(err, stats) {
if (err) {
console.log(err.toString());
}
console.log(stats.toString());
callback();
});
});
gulp.task('watch', function(done) {
browserSync.init({
notify: false,
proxy: settings.urlToPreview,
ghostMode: false
});
gulp.watch('./**/*.php', function(done) {
browserSync.reload();
done();
});
gulp.watch(settings.themeLocation + 'css/**/*.css', gulp.parallel('waitForStyles'));
gulp.watch([settings.themeLocation + 'js/modules/*.js', settings.themeLocation + 'js/scripts.js'], gulp.parallel('waitForScripts'));
done();
});
gulp.task('waitForStyles', gulp.series('styles', function() {
return gulp.src(settings.themeLocation + 'style.css')
.pipe(browserSync.stream());
}))
gulp.task('waitForScripts', gulp.series('scripts', function(cb) {
browserSync.reload();
cb()
}))
webpack.config.js:-
const path = require('path'),
settings = require('./settings');
module.exports = {
entry: {
App: settings.themeLocation + "js/scripts.js"
},
output: {
path: path.resolve(__dirname, settings.themeLocation + "js"),
filename: "scripts-bundled.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
},
mode: 'development'
}
settings.js:
exports.themeLocation = '/wp-content/themes/fictional-university-theme/';
exports.urlToPreview = 'localhost/imuni';
I had the same error the solution is simple add a DOT before /wp-content/:
exports.themeLocation = './wp-content/themes/fictional-university-theme/';
exports.urlToPreview = 'localhost/imuni';

Gulp webpack load jquery only once

I've got two Gulp tasks in my gulpfile.js. It's used for a website.
The first one compiles with webpack the main js file, used on all pages of the site (mainly visuals), and combines it to a single file.
gulp.task('scripts', function(callback) {
let firstBuildReady = false;
function done(err, stats) {
firstBuildReady = true;
if (err) { // hard error, see https://webpack.github.io/docs/node.js-api.html#error-handling
return; // emit('error', err) in webpack-stream
}
gulplog[stats.hasErrors() ? 'error' : 'info'](stats.toString({
colors: true
}));
}
let options = {
output: {
publicPath: '/js/',
filename: isDevelopment ? '[name].js' : '[name]-[chunkhash:10].js'
},
watch: isDevelopment,
devtool: isDevelopment ? 'cheap-module-inline-source-map' : false,
module: {
loaders: [{
test: /\.js$/,
//include: path.join(__dirname, "app/src/scripts/modules"),
loader: 'babel-loader',
query: {
presets: ["env"]
}
}]
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
]
};
if (!isDevelopment) {
options.plugins.push(new AssetsPlugin({
filename: 'scripts.json',
path: __dirname + '/app/manifest',
processOutput(assets) {
for (let key in assets) {
assets[key + '.js'] = assets[key].js.slice(options.output.publicPath.length);
delete assets[key];
}
return JSON.stringify(assets);
}
}));
}
return gulp.src(jsSRC)
.pipe(plumber({
errorHandler: notify.onError(err => ({
title: 'Scripts',
message: err.message
}))
}))
.pipe(named(function(file){
return 'app'
}))
.pipe(webpackStream(options, null, done))
.pipe(gulpIf(!isDevelopment, uglify()))
.pipe(gulp.dest(jsDIST))
.on('data', function() {
if (firstBuildReady) {
callback();
}
});
});
The second one compiles each js module as a single file - some js scripts, used on special pages. These scripts are included only there where needed.
gulp.task('webpack', function(callback) {
let firstBuildReady = false;
function done(err, stats) {
firstBuildReady = true;
if (err) {
return;
}
gulplog[stats.hasErrors() ? 'error' : 'info'](stats.toString({
colors: true
}));
}
let options = {
output: {
publicPath: '/js/',
filename: isDevelopment ? '[name].js' : '[name]-[chunkhash:10].js'
},
watch: isDevelopment,
devtool: isDevelopment ? 'cheap-module-inline-source-map' : false,
module: {
loaders: [{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ["env"]
}
}]
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
]
};
if (!isDevelopment) {
options.plugins.push(new AssetsPlugin({
filename: 'webpack.json',
path: __dirname + '/app/manifest',
processOutput(assets) {
for (let key in assets) {
assets[key + '.js'] = assets[key].js.slice(options.output.publicPath.length);
delete assets[key];
}
return JSON.stringify(assets);
}
}));
}
return gulp.src('app/src/scripts/modules/*.js')
.pipe(plumber({
errorHandler: notify.onError(err => ({
title: 'Webpack',
message: err.message
}))
}))
.pipe(named())
.pipe(webpackStream(options, null, done))
.pipe(gulpIf(!isDevelopment, uglify()))
.pipe(gulp.dest(jsDIST))
.on('data', function() {
if (firstBuildReady) {
callback();
}
});
});
But I have to include Jquery in every single file for the second task, otherwise it's not compiled. But Jquery is included in the main app.js file.
How can I solve it?
Thanks
Since it sounds like you're using a somewhat exotic way of loading JS in your application (instead of approaches like require.ensure), your easiest option may be to use Webpack externals when building your individual modules. Your main script/page will have to ensure that jQuery is globally exposed (like under window.$ or window.jQuery). Then, for your webpack config, include something like this:
{
// ...
externals: {
jquery: '$'
}
}
This will substitute $ for all require('jquery') calls instead of including jquery in each JS bundle.

Grunt-webpack wildcard on 'entry'

I'm using Grunt to compile page level JS assets.
webpack: {
build: {
entry: {
// Add your page JS here!
home: "./assets/scripts/tmp/Pages/home.js",
events: "./assets/scripts/tmp/Pages/events.js"
},
output: {
path: "./public/scripts"
}
}
}
This is how I'm currently doing it, but I'd like to do something like:
webpack: {
build: {
entry: "./assets/scripts/tmp/Pages/*",
output: {
path: "./public/scripts"
}
}
}
However this fails with an "ERROR in Entry module not found:" error.
I've tried SRC and DEST options instead but they didn't seem to even compile the files :S
Thanks in advance
The entry option doesn't support wildcards, but grunt does. You can use grunts wildcard support to construct an object for the entry option:
var pagesBase = path.resolve("assets/scripts/tmp/Pages");
build: {
entry: grunt.file.expand({ cwd: pagesBase }, "*").reduce(function(map, page) {
map[path.basename(page)] = path.join(pagesBase, page);
return map;
}, {}),
output: {
path: "./public/scripts",
filename: "[name].js" // You also need this to name the output file
}
}
grunt.file.expand just returns an array of all matching files in the pages directory. Array.prototype.reduce is used to convert the array into an object.
Note: To make your example complete you also need to include [name] in the output.filename option.
To anyone else looking for a simple fact... this is what I used:
webpack: {
build: {
entry: {
"home-page": "./" + path + "scripts/tmp/Pages/home-page.js",
"event-page": "./" + path + "scripts/tmp/Pages/event-page.js",
"performer-page": "./" + path + "scripts/tmp/Pages/performer-page.js",
"order-page": "./" + path + "scripts/tmp/Pages/order-page.js",
"support-page": "./" + path + "scripts/tmp/Pages/support-page.js"
},
output: {
path: "public/scripts",
filename: "[name].js"
}
}
}
Similar to Tobias K. answer but with a working example :
var config = {
...
webpackFiles: {}
};
//Dynamically create list of files in a folder to bundle for webpack
grunt.file.expand({ cwd: 'my/folder/' }, "*").forEach(function(item){
config.webpackFiles[item.substr(0,item.indexOf('.js'))] = './my/folder/' + item;
});
And then in your grunt task use it like this:
webpack: {
build: {
entry: config.webpackFiles,
output: {
path: "<%= config.jsDest %>/",
filename: "[name].js"
},
module: {
...
}
}
},
The only downside is that if you want to add specific file to this build (for example bundle app.js) , you will have to add this to the webpackFiles variable like so
//Dynamically create list of view to bundle for webpack
config.webpackFiles.App = './' + config.jsSrc + '/App.js';
grunt.file.expand({ cwd: 'Static/js/src/views/' }, "*").forEach(function(item){
config.webpackFiles[item.substr(0,item.indexOf('.js'))] = './Static/js/src/views/' + item;
});

Categories

Resources