I have created module A which is a component library for my React App. Which I plan on using on module B which is my actual React App.
I have an index.js whereby I export my components from module A by using loadable components in the following fashion
import loadable from '#loadable/component'
export const Theme = loadable(() => import('./Theme'))
export const OtherComponent = loadable(() => import('./OtherComponent'))
export const OtherComponent2 = loadable(() => import('./OtherComponent2'))
I therefore build and deploy module A to npm by using the following webpack configuration
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin")
const LoadablePlugin = require('#loadable/webpack-plugin')
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
minimize: true,
concatenateModules: false,
minimizer: [new TerserPlugin({
terserOptions: {
keep_fnames: true
}
})],
},
entry: {
main: './src/components/index.js',
},
output: {
publicPath: '/',
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
library: "myComponentLibrary",
libraryTarget: "umd",
globalObject: "this"
},
externals: {
react: {
root: 'React',
commonjs: 'react',
commonjs2: 'react',
amd: 'react',
},
},
plugins: [new CleanWebpackPlugin(), new LoadablePlugin()],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
cacheDirectory: true
}
}
],
},
{
test: /\.(jpe?g|png|gif|svg)$/,
type: 'asset/inline'
},
]
}
}
I expected that when I npm install module A on module B to be able to import and render my components but instead I get the following error.
loadable-components: failed to asynchronously load component { fileName: undefined, chunkName: undefined, error: 'Loading chunk 2661 failed.\n(error: 2661.js)' }
Please provide some guidance on how I can solve this issue
If everything is working well on development but for production is not and you face with this error, Add this <base href="/"/> to head of index.html:
<!DOCTYPE html>
<html>
<head>
<base href="/"/>
<meta charset="utf-8" />
<title>Foo project</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
Now, I think everything is working well.
This problem is because of html5 routing, you can search about it.
Related
There are two separate repo:
Library repo
React Web application
library provide 2 functions
func1 without using web-worker
func2 using web-worker
// func2
export const func2 = () => {
//.....
const worker = new Worker(new URL('./web-worker/worker.ts', import.meta.url));
//.....
}
bundle files from webpack5
then import it in react web application
func1 works fine
but got this error when I calling the func2
below is webpack config for the library
const path = require('path');
const config = {
mode: 'production',
entry: './src/index.ts',
module: {
rules: [
{
test: /.ts$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['#babel/preset-env', '#babel/preset-typescript'],
},
},
],
},
output: {
filename: "index.js",
path: path.resolve(__dirname, 'dist'),
publicPath: "/",
library: {
type: 'umd',
},
},
resolve: {
extensions: ['.ts', '.js'],
},
devtool: 'source-map',
};
module.exports = config
I try to create react library with Webpack.
Simplified structure of library:
+ Base
|
|--- index.js
export * from "./Base";
|--- Base.jsx
export const Base = { "base_property_1": "base_property_1_value" };
+ Data
|
|--- index.js
export * from "./Data";
|--- Data.jsx
import { Base } from "../Base";
export const Data = { "data_property_1": "data_property_1_value", ...Base };
+ Red
|
|--- index.js
export * from "./Red";
|--- Red.jsx
import React from "react";
export const Red = () => <div>[Red]</div>;
I try to build library with this webpack.library_create.config.js:
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: {
"Base": path.resolve(__dirname, "./library_src/Base"),
"Data": path.resolve(__dirname, "./library_src/Data"),
"Red": path.resolve(__dirname, "./library_src/Red"),
},
externals: {
"react": "commonjs react",
"react-dom": "commonjs react-dom",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
optimization: {
minimize: false,
splitChunks: {
chunks: "all",
minChunks: 2,
minSize: 1,
},
},
output: {
clean: true,
filename: "[name]/index.js",
libraryTarget: "umd",
library: "Astra",
path: path.resolve(__dirname, "./node_modules/#a/library"),
umdNamedDefine: true,
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
target: "web",
};
Then I try to start project:
+ dist
|--- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
</head>
<body>
<div></div>
<script src="./bundle.js"></script>
</body>
</html>
+ library_use
|--- index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { Data } from "#a/library/Data";
import { Red } from "#a/library/Red";
console.log(Data);
const App = () => <div>App <Red /></div>;
ReactDOM.render( <App />, document.querySelectorAll( "body > div" )[ 0 ] );
using webpack.library_use.config.js:
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: path.resolve(__dirname, "./library_use/index.jsx"),
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
resolve: {
extensions: ["*", ".js", ".jsx"],
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
plugins: [ new webpack.HotModuleReplacementPlugin() ],
devServer: {
hot: true,
open: true,
port: 3020,
static: [
path.resolve(__dirname, "./dist"),
{
directory: path.resolve(__dirname, "./dist"),
serveIndex: true,
watch: true,
},
],
},
};
And I getting error: Uncaught TypeError: Cannot read properties of undefined (reading 'Data')
i.e. Data is not initialized.
If I remove dependency Base from Data, then Data initialized:
+ Data
|--- Data.js
// import { Base } from "../Base";
// export const Data = { "data_property_1": "data_property_1_value", ...Base };
export const Data = { "data_property_1": "data_property_1_value", };
How to set up webpack.library_create.config.js to build my library with dependencies to work my project (with webpack.library_use.config.js)?
This project on git https://github.com/rosinfotech/issue_210921_webpack_library_dependencies
Eventually, nobody answered my issue. Even collaborators of the Webpack projects define the issue as discussion and suppose to figure out in Webpack code to resolve this problem myself https://github.com/webpack/webpack/discussions/14303#discussioncomment-1376696 But I chosen another way – the Rollup, which resolved my task very effectively https://github.com/rosinfotech/issue_210921_webpack_library_dependencies/tree/rollup
I'm using setup from getting-started webpack page:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'development',
entry: './src/index.js',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
}),
new MiniCssExtractPlugin(),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true //clean dist folder before each build
},
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif|ico)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
};
And this one works OK if I include import './style.css'; at the top of src/index.js. Inside of the produced dest/index.html I get the line where the extracted CSS style is generated as <link href="main.css" rel="stylesheet">.
Now what I want is to remove that line import './style.css'; at the top of src/index.js and use instead of that one <link rel="stylesheet" type="text/css" href="style.css"> that I will place inside the template src/index.html.
When doing this, generated dest/index.html gets correctly the line <link rel="stylesheet" type="text/css" href="b88d04fba731603756b1.css"> and the file dest/b88d04fba731603756b1.css is generated, but it's content is wrong as I get this instead of real css styles:
// extracted by mini-css-extract-plugin
export {};
Is there a way to use html-loader plugin together with MiniCssExtractPlugin, so that I do not need to import css inside js files but instead import it inside html template?
I'm pretty new to React and I'm having some issues with my project. I feel like I'm going insane, the simplest things are not working as I'd expect them to. Having re-read my code what feels like hundreds of times at this point, I still can't figure out for the life of me what the problem is. Specifically, I was testing a simple button with an onClick function (onClick={() => console.log("test")}). The button renders fine, but clicking it doesn't log anything in the console.
I'm also using Webpack and Express (though I doubt Express is causing any issues at this point).
I've tried searching for similar issues but nothing has helped thus far.
Below is my index.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import styles from './index.module.scss';
function App() {
return (
<>
<button onClick={() => console.log("Working")} >Test</button>
</>
)
}
ReactDOM.render((
<App />
), document.getElementById('root'));
module.hot.accept();
Here is my index.ejs file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.bundle.js"></script></head>
<body>
<div id="root">
</div>
<script src="main.bundle.js"></script>
<% for (var css in htmlWebpackPlugin.files.css) { %>
<link href=".<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet" />
<% } %>
</body>
</html>
Here's the webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = (env, args) => {
return {
entry: './src/index.jsx',
...(options()),
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: plugins(),
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
}
};
};
const plugins = () => {
const resOut = path.resolve(__dirname, 'dist');
const plugs = [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [resOut],
dry: false,
}),
new HtmlWebpackPlugin({
hash: true,
filename: path.resolve(resOut, 'index.html'),
template: './src/index.ejs',
})
]
return plugs;
}
const options = () => {
const ops = {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
compress: true,
port: 9000,
historyApiFallback: true,
openPage: '',
hot: true,
proxy: {
'/api/**' : {
target: 'http://localhost:3000',
secure: false,
}
},
allowedHosts: [
'localhost',
]
},
};
return ops;
}
I'm also using this small batch script to run the webpack dev server and start Express:
#echo off
cd %~dp0
start cmd.exe /k npm run serve
start node ./express/app.js
npm run build -- --watch
I'm not sure if this is related, but the React dev tools show that 'App' is rendered 3 times. I'm not sure if that is some quirk with the dev tools or if it may be causing some issues. Again, I've tried to find if anyone has experienced anything similar, but couldn't find anything.
I'm happy to share any other information that might be needed to get to the bottom of this. Thanks in advance for any help you can give.
You're importing index.bundle.js twice , try uncommenting the script in index.ejs since webpack automatically imports it , this should fix your issue
Firstly, I scaffolded a vuejs project as a test container from vue-cli.
Then I create a npm package "vue-npm-example" from a Vuejs component in local environment and imported in the above testing project.
In the package,
I ran npm link and in the project I ran npm link vue-npm-example,
Example.vue
<template>
<div id="vue-npm-example">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'vue-npm-example',
data() {
return {
msg: "Welcome to Your Vue.js App"
}
},
mounted() {
console.log('this is in compoennt file')
}
};
</script>
<style lang="scss">
</style>
main.js
import Example from './components/Example.vue'
export function install(Vue, options) {
options = {
installComponents: true
}
if (options.installComponents) {
Vue.component('vuejs-example', Example)
}
}
export default Example
webpack.config.js
let path = require('path')
let webpack = require('webpack')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'index.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax'
]
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// Since sass-loader (weirdly) has SCSS as its default parse mode, we map
// the "scss" and "sass" values for the lang attribute to the right configs here.
// other preprocessors should work out of the box, no loader config like this necessary.
'scss': [
'vue-style-loader',
'css-loader',
'sass-loader'
],
'sass': [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax'
]
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js',
'#': resolve('src')
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map',
node: {
fs: 'empty'
},
watch: true
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
// new webpack.optimize.UglifyJsPlugin({
// sourceMap: true,
// compress: {
// warnings: false
// }
// }),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
Then in the testing project
I do
import Vue from 'vue'
import Example from 'vue-npm-example'
Vue.component('example', Example)
and use it like
<example></example>
I got error:
[Vue warn]: Failed to mount component: template or render function not defined.
I set the vue alias in the webpack config files for both package and project. The package got pulled in correctly because when I do console.log() in the package's main.js, it logs in the testing project. But no matter what I tried, the component in the package still won't work in the testing project.
Any suggestions?
npm link create an symlink, but when import the local npm package, I need to specify the full address of the package. In my case, I have to do import customComponent from './node_modules/custom-component/dist/index.js'
import customComponent from 'custom-component`.
I'd use npm pack instead (some downsides of using npm link to test your npm package locally: https://blog.vcarl.com/testing-packages-before-publishing/)
In the package
Run npm pack
In the project
Run npm install (path-to-package)/package-name-0.0.0.tgz
Then import/install the package in your main.js:
import MyPackage from 'package-name'
// This runs your 'install' method which registers your component globally with Vue.component(...)
Vue.use(MyPackage);
Some useful links
Packaging Vue components for npm: https://v2.vuejs.org/v2/cookbook/packaging-sfc-for-npm.html
Vue npm walkthrough: https://www.telerik.com/blogs/vuejs-how-to-build-your-first-package-publish-it-on-npm
Global component registration: https://v2.vuejs.org/v2/guide/components-registration.html#Global-Registration