Extending webpack output - javascript

I'm trying to write Webpack plugin which will generate alias for each chunk defined inside entry. My idea was to generate alias which then I could use inside library property, just like we can use [name] or [id] in output. So my newly created plugin looks next:
function Aliasify(options) {
this.options = options;
}
Aliasify.prototype.apply = function(compiler) {
var self = this;
compiler.plugin("compilation", function(compilation) {
compilation.plugin("optimize", function() {
compilation.chunks.forEach(function(chunk) {
var alias = chunk.name.replace(self.options.searchFor, self.options.replaceWith);
chunk.alias = alias;
});
}
};
This generates property alias for every chunk defined in entry.
Using the [alias] inside library property exposes the every chunk as '[alias]' which isn't what i wanted.
library: ['gravity', 'gateway', '[alias]']
I hoped this will generate chunks with exposed master and slave which is the value of alias property inside chunk. My config.js looks next:
module.exports = {
entry: {
'master': './master.js',
'master.min': './master.js',
'slave': './slave.js',
'slave.min': './slave.js'
},
plugins: [
new Clean(['dist']),
new Uglify({
include: /\.min\.js$/i,
minimize: true
}),
new Aliasify({
searchFor: '.min',
replaceWith: ''
})
],
output: {
filename: '[name].js',
path: __dirname + '/dist',
library: ['gravity', 'gateway', '[alias]'],
libraryTarget: 'umd',
umdNamedDefine: true
}
};
So the bottom line is:
I want files created as master.js, master.min.js, slave.js and slave.min.js - this is ok using the name in filename prop
I want library to be exposed as master inside master.js, master.min.js and slave in slave.js, slave.min.js , but every chunk gets exposed as [alias]

Found workaround for this. According to documentation webpack will accept an array of configurations. I used this to bundle files with completely different settings.
module.exports = [
{
entry: {
'index' : './index.js',
'master': './master.js',
'slave': './slave.js',
'plugin-storage': './plugin-storage.js',
},
plugins: [
new Clean(['dist']),
],
output: {
filename: '[name].js',
path: __dirname + '/dist',
library: ['gravity', 'gateway', '[name]'],
libraryTarget: 'umd',
umdNamedDefine: true
}
},
{
entry: {
'index' : './index.js',
'master': './master.js',
'slave': './slave.js',
'plugin-storage': './plugin-storage.js'
},
plugins: [
new Uglify({
include: /\.js$/i,
minimize: true
})
],
output: {
filename: '[name].min.js',
path: __dirname + '/dist',
library: ['gravity', 'gateway', '[name]'],
libraryTarget: 'umd',
umdNamedDefine: true
}
}
];

Related

Webpack dynamic import each imported module

This may be an x/y question, so here goes!
Background:
I'm trying to run a comparison of two versions of a JS library to measure the benefits of its side-effect free tree-shaking modules.
My plan was to make two .html pages, one with old.js, and another importing specific modules (i.e. import {mod1, mod2} from "new.js")
Webpack Chunk Names
Ideally, I'd like each individual module to be placed into its own chunk so I can document how much each module "weighs".
I see webpack has an option to add /* webpackChunkName: "my-chunk-name" */ inside of an import.
Question:
Is it possible to dynamically import an individual property/module while specifying its name to generate its own chunk?
I've tried using this code below, but it combines them into a single chunk based on the first mod1 chunkname.
document.getElementById('mod1').onclick = function () {
import(/* webpackChunkName: "mod1" */ 'new.js').then(
(lib) => {
lib.mod1()
}
);
};
document.getElementById('mod2')!.onclick = function () {
import(/* webpackChunkName: "mod2" */ 'new.js').then(
(lib) => {
lib.mod2()
}
);
};
webpack.config.js
// Generated using webpack-cli https://github.com/webpack/webpack-cli
import { Configuration } from 'webpack';
import 'webpack-dev-server';
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const isProduction = process.env.NODE_ENV == 'production';
const config = {
// An entry point is the root JS file associated with a HTML route
entry: {
old: './src/old.ts',
new: './src/new.ts',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
open: false,
host: 'localhost',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
},
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
// output name (URL path)
filename: 'old.html',
// the template property to the HTML template
template: path.resolve(__dirname, 'src', 'old.html'),
// associate it with one or more of the entry points with the chunks property.
chunks: ['old'],
}),
new HtmlWebpackPlugin({
// output name (URL path)
filename: 'new.html',
// the template property to the HTML template
template: path.resolve(__dirname, 'src', 'new.html'),
// associate it with one or more of the entry points with the chunks property.
chunks: ['new'],
}),
new HtmlWebpackPlugin({
// output name (URL path)
filename: 'index.html',
// the template property to the HTML template
template: path.resolve(__dirname, 'index.html'),
// associate it with one or more of the entry points with the chunks property.
chunks: [],
}),
// Add your plugins here
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
],
performance: {
hints: false,
},
module: {
rules: [
{
test: /\.(ts|tsx)$/i,
loader: 'ts-loader',
exclude: ['/node_modules/'],
},
{
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
type: 'asset',
},
// Add your rules for custom modules here
// Learn more about loaders from https://webpack.js.org/loaders/
],
},
resolve: {
extensions: ['.ts', '.js'],
},
experiments: {
topLevelAwait: true,
},
};
module.exports = () => {
if (isProduction) {
config.mode = 'production';
} else {
config.mode = 'development';
}
return config;
};
I think the reason webpack combines the same module(new.js) into a single chunk is because MergeDuplicateChunksPlugin is used.
Its functionality is very well described by its name and in this situation it can be seen in action: the mod1 and mod2 chunks are using the same new.js module, so they're fundamentally the same.
Fortunately, this plugin is behind a flag and it can be deactivated by modifying your configuration as follows:
config = {
/* ... */
optimization: {
mergeDuplicateChunks: false,
},
/* ... */
}
With the above configuration, you should now see the new.js module being duplicated in two different chunks - mod1 and mod2.

Custom React component library does not work in not node running enviroment using global

I have a react component library that I try to make work with applications not running in a node environment, the compiled version is included via a script tag.
Since it does not support imports, I am trying to assign it to the window.
When importing compiled js to the consumer project the following error:
global is not defined pops up and the window does not contain the Components object that I've assigned to the window.
Is there at all possible to make it work for both node and web environments?
Components: (index.ts)
import { Button } from "./src/components/Button/Button";
export { default as Button } from './src/components/Button/Button';
window.Components = {
Button,
};
Webpack:
/* eslint-disable import/no-extraneous-dependencies */
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { merge } = require('webpack-merge')
const { resolve } = require("path");
const TerserPlugin = require('terser-webpack-plugin');
const DtsBundleWebpack = require('dts-bundle-webpack')
const nodeExternals = require('webpack-node-externals');
const commonConfig = require('./webpack.common.config')
module.exports = merge(commonConfig , {
entry: resolve(__dirname, './', 'index.ts'),
mode: 'production',
target: 'node',
node: {global: true},
output: {
filename: '[name].js',
chunkFilename: '[name].js',
libraryTarget: 'umd',
library: 'Components'
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
}),
],
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'initial',
},
async: {
test: /[\\/]node_modules[\\/]/,
name: 'async',
chunks: 'async',
minChunks: 4,
},
},
},
},
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
plugins: [
new CleanWebpackPlugin(),
new DtsBundleWebpack({
name: "components",
main: resolve(__dirname, './', 'index.d.ts'),
out: resolve(__dirname, './', 'dist/main.d.ts'),
})
],
});

Webpack, optimization chunking gives "Conflict: Multiple chunks emit assets to the same filename" error

Info
I am trying to generate my own webpack config and have some problems getting it working.
Problem
When trying to use optimization to split files into chunks I get the a error like underneath
Error: Conflict: Multiple chunks emit assets to the same filename static/js/bundle.js (chunks main and vendors-node_modules_react-hot-loader_patch_js-node_modules_react_jsx-dev-runtime_js-node_mod-4610d2)
If I remove the optimization section it works but without chunking. I have looked to the create react app webpack.config.js to get something to reference while generating this.
As you can see they have the optimization section working with chunking in both development and production. Why do I get the conflict error when using it?
Code
Minified/simplified version of my config (runtimeChunk disabled, as it also gives the same conflict error)
webpack.config.js
module.exports = () => {
process.env.NODE_ENV = "development";
process.env.BABEL_ENV = "development";
return {
mode: "development",
entry: ["react-hot-loader/patch", "./src"],
output: {
path: undefined,
publicPath: "/",
filename: "static/js/bundle.js",
chunkFilename: "static/js/[name].chunk.js",
},
optimization: {
minimize: false,
splitChunks: {
chunks: "all",
name: false
},
// runtimeChunk: {
// name: (entrypoint) => `runtime-${entrypoint.name}`,
// },
},
resolve: {
modules: [path.join(__dirname, "src"), "node_modules"],
alias: {
"react-dom": "#hot-loader/react-dom",
},
},
module: {
rules: [
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: path.resolve(__dirname, "./src"),
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: path.resolve(__dirname, "./public/index.html"),
}),
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
compress: true,
hot: true,
contentBase: "./build",
historyApiFallback: true,
},
devtool: "inline-source-map",
};
};
.babelrc
{"presets": [["react-app", {"runtime": "automatic"}]]}
Got it to work had to change filename: "static/js/bundle.js" to filename: "static/js/[name].js"
output: {
path: undefined,
publicPath: "/",
filename: "static/js/[name].js",
chunkFilename: "static/js/[name].chunk.js",
}
If you are working on an ejected Create React App and you get a similar error
Multiple chunks emit assets to the same filename static/js/bundle.js
(chunks main and runtime-main)
you can just change the filename property in the output configuration from
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
to
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/[name].js',
Where the [name] placeholder is giving a different name to each output bundle instead of a fixed one.
In my case it was caused by the runtime-main.js file which I was generating with the runtimeChunk property inside optimization.
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},

Webpack renames functions

Hello i have some problems with webpack. I have this config for webpack
module.exports = {
name: "front",
mode: "production",
context: path.resolve(__dirname, 'src'),
entry: [
'./jquery/photoswipe.addon_offer_and_order.min.js',
'./jquery/photoswipe.min.js',
'./jquery/photoswipe-ui-default.min.js',
'./deprecated.js',
'./index.js',
],
output: {
filename: "index.min.js",
path: path.resolve(__dirname, 'dist')
},
optimization: {
moduleIds: 'named'
}
}
All good but i have a deprecated.js and have all deprecated functions in it...
Example:
function updateSearchCharacteristic(url, category_id) {
console.warn("This method is deprecated please use shopSearch.updateCharacteristic()");
return shopSearch.updateCharacteristic(url, category_id);
}
function moveBlockAnfrageGuest() {
console.warn("This method is deprecated please use shopUser.moveOrderAndOfferLinkForGuest()");
return shopUser.moveOrderAndOfferLinkForGuest();
}
Webpack rename all these functions, if someone used the old functions, he does not see errors and the return does not work ..
How not to rename functions in this file, but compress
I resolved this problem :
npm install -D script-loader terser-webpack-plugin
Added a module into config and 'require' a plugin
const TerserPlugin = require('terser-webpack-plugin')
module: {
rules: [
{
test: /deprecated.js/,
use : [
{
loader: 'script-loader',
options:{
plugins: [
new TerserPlugin({
terserOptions: {
keep_fnames: true,
}
})
]
}
}
]
}
]
}

How to change the request path in webpack

Im trying to integrate PhotoSwipe into my current Project
this is the webpack.config.js
module.exports =
{
entry: './input.js',
output:
{
path: 'js/',
filename: 'output.js'
},
resolve:
{
alias:
{
'photoswipe_js': './node_modules/photoswipe/dist/photoswipe.js',
'photoswipe_ui_default': './node_modules/photoswipe/dist/photoswipe-ui-default.js'
}
},
watch: true
};
this is my main file
require(['photoswipe_js', 'photoswipe_ui_default'], function( PhotoSwipe, PhotoSwipeUI_Default )
{
console.log(PhotoSwipe);
console.log(PhotoSwipeUI_Default);
});
for some reason its trying to find the compiled file from the project root
like
'/1.output.js'
I need it to try to fetch the output file from
'/bundles/mybundle/js/1.output.js'
instead, how can I do that?
Add publicPath to your output object :
module.exports =
{
...
output:
{
path: 'js/',
filename: 'output.js',
publicPath: '/bundles/mybundle/js/'
},
...
};

Categories