I am developing a npm package and use there webpack 5.
This is my config
const path = require('path') // resolve path
module.exports = {
mode: 'development',
resolve: {
extensions: ['.js', '.jsx'], // we can import without typing '.js or .jsx'
},
entry: {
index: path.join(__dirname, 'src/index.jsx'),
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index.js', // for production use [contenthash], for developement use [hash]
library: {
type: 'module',
},
environment: { module: true },
},
experiments: {
outputModule: true,
},
devtool: 'eval',
module: {
rules: [
{
test: /\.(css|scss|sass)$/,
use: [
{
loader: 'style-loader', // creates style nodes from JS strings
},
{
loader: 'css-loader', // translates CSS into CommonJS
},
{
loader: 'sass-loader', // compiles Sass to CSS
},
],
},
{
test: /\.(js|jsx)$/,
exclude: /nodeModules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
exclude:
/images/ /* dont want svg images from image folder to be included */,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'fonts/',
name: '[name][hash].[ext]',
},
},
],
},
],
},
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
plugins: [],
}
When I want to import components from this library I get this error:
Failed to resolve import "React" from "..my-package/dist/index.js". Does the file exist?
I think this has something to do with externals react and react-dom. But when I remove the externals from webpack config I get the error that react should be only installed once when using react hooks.
In my main application I am using vite for bundeling but I tried it also with create-react-app and have the same problem.
My Library Component:
import React, { useState } from 'react'
const App = () => {
const [counter, setCounter] = useState(0)
return <div>{counter}</div>
}
export default App
This is how I am using it with npm-link:
import React from "react";
import Test from "my-package";
const App = () => {
return <Test />;
};
export default App;
Here is the repo with the code https://github.com/guitar9/webpack-bug
Related
I had a working storybook but had to update to webpack5 to try and fix a sass issue... but that is now breaking the stories and I am not sure how to fix it.
I have a main.js on my <root>/.storybook folder:
const path = require('path');
module.exports = {
core: {
builder: 'webpack5',
},
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: "[name].[ext]",
outputPath: 'fonts/'
}
}
]
});
config.module.rules.push({
test: /\.(js|ts|tsx)$/,
use: [
{
loader: 'babel-loader',
}
],
});
config.module.rules.push(
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
include: path.resolve(__dirname, "../")
});
config.resolve.extensions = ['.ts', '.tsx', '.js'];
return config;
},
stories: ['../src/stories/storybook.js'] // <-- this used to work
};
My <root>/src/stories/storybook.js:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import { addDecorator } from '#storybook/react';
import { withA11y } from '#storybook/addon-a11y';
import StyleguideStory from './styleguide';
import './reset.css';
addDecorator(withA11y);
StyleguideStory('Styleguide');
And Basically styleGuide is a folder (<root>/src/stories/styleguide) with this index inside:
import Story1 from './Story1';
...
export default function StyleguideStory(nameRoot) {
Story1(nameRoot);
...
}
My stories are normally folders (<root>/src/stories/styleguide/<folders>) with an index:
import React from 'react';
import { storiesOf } from '#storybook/react';
import StoryExample from './StoryExample';
import StoryDoc from './StoryDoc';
export default function Story1(nameRoot) {
const story = storiesOf(`${nameRoot}/Story1`, module);
story.add('Examples', () => <StoryExample />);
story.add('Props', () => <StoryDoc />);
}
And each story is a normal function component... these all used to work before going to webpack, but I am not sure if I configured it wrong, or if this structure is not supported anymore....
I am begining with React using Webpack to make the configuration. I made all step by step. No error message on console or browser but the h1 that I want to insert doesn't appear.
I know that React is v.18 but I am using React v.17
App.jsx
import React from 'react'
const App = () => {
return (
<h1>Holaaa</h1>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app.jsx';
ReactDOM.render (<App/>, document.getElementById ("Hello"))
webpack.config.js
const path = require ("path");
const HtmlWebpackPlugin = require ('html-webpack-plugin')
const MiniCssExtractPlugin = require ('mini-css-extract-plugin')
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve (__dirname, 'dist'),
filename: "bundle.js"
},
mode: "development",
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{test: /\.(js|jsx)$/,
exclude: "/node_modules",
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}},
{test: /\html$/,
use: [
{loader: 'html-loader'}
] },
{test: /\.sa[ac]ss$/,
use: ['css-loader',
'style-loader',
'sass-loader']}
]
},
plugins: [
new HtmlWebpackPlugin ({
inject: true,
template: "./public/index.html",
filename: '/menu.html'
}), new MiniCssExtractPlugin ({
filename: "[name].css"
})
]
}
index.html
<title>Document</title>
</head>
<body>
<div id="Hello"> </div>
</body>
</html>
I found the solution. It was the filename config. Was /menu.html. So when localhost was with /menu.html, React render the h1 element.
So, to make render I only have to change /menu.html for index.html in the filename config, refresh and was done!
I am learning how to make custom component or library and publish on npm. I found a free tutorial here:
https://www.udemy.com/course/your-custom-react-component/learn/lecture/13561884#overview
github link
https://github.com/davidcsejtei/custom-button
But I am facing one problem .he created a simple component custom button
import React, { Component } from 'react';
import '../style/button.scss';
export default class CustomButton extends Component {
render() {
return(
<button>Custom button</button>
);
}
}
but when we create a build it include react library in his build so now it's size increase to 7.5kb.It should be below 2kb as it is small component and it is used in another project
In other ways custom component use react dependency from there parent component . Can we remove this react dependency in build ?
Here is the webpack config:
const webpack = require('webpack');
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'custom-button.js',
library: 'custom-button',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ["#babel/preset-env", "#babel/preset-react"]
}
}
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
}
]
},
optimization: {
minimizer: [new UglifyJsPlugin()]
},
plugins: [
new CleanWebpackPlugin(['dist'])
]
}
any update ?
In order to exclude react out of your build with webpack, you just simply declare externals in your configuration like below:
externals: {
react: "react",
}
As you output with your umd module above, it will require react as the dependency in the build which means it will consume the react at the parent (consuming) repo:
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("react"));
// ...
I have a VueJS 2 application in which I'm trying to load specific components depending on different flags in a config JSON file.
My file structure is as follows:
root
config.json
> src
>> client
>>> components
>>>> dashboard
>>>>> MenuBarBrand.vue
> product
>> product01
>>> client
>>>> components
>>>>> branding
>>>>>> MenuBrand.vue
>> product02
>>> client
>>>> components
>>>>> branding
>>>>>> MenuBrand.vue
At the moment I've tried 2 different methods:
Using the webpack import syntax in my MenuBarBrand component:
<template>
<b-navbar-brand href="/dashboard">
<MenuBrand />
</b-navbar-brand>
</template>
<script>
import config from '../../../../config.json';
const MenuBrand = import(`../../../../product/${config.product}/components/branding/MenuBrand`);
export default {
name: 'MenuBarBrand',
components: {
'MenuBrand',
},
}
</script>
which results in the following error:
or the Vue Async Components local registration syntax:
<template>
<b-navbar-brand href="/dashboard">
<MenuBrand />
</b-navbar-brand>
</template>
<script>
import config from '../../../../config.json';
export default {
name: 'MenuBarBrand',
components: {
'MenuBrand': () => import(`../../../../product/${config.product}/components/branding/MenuBrand`)
},
}
</script>
which results in the following error:
Are there any clear errors that I'm making or are there any specific babel/webpack plugins needed to enable this functionality?
Edit: Adding my loader configuration based on a comment I received:
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: [
'vue-style-loader',
'css-loader',
'sass-loader',
],
sass: [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax',
],
},
},
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]',
},
},
{
test: /\.css$/,
loader: 'style-loader!css-loader',
},
{
test: /\.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader?name=fonts/[name].[ext]',
},
],
},
I solved this through an alternate method.
In the webpack config I pull in my product flag and create a path:
const config = require('./config.json');
const fs = require('fs');
const path = require('path');
const productPath = path.resolve(__dirname, 'product', config.product);
Then I set up an alias in the webpack config using this path:
resolve: {
alias: {
menuBrandingPath: productPath,
},
extensions: ['*', '.js', '.vue', '.json'],
},
This path is then used in my Vue component to import the proper component:
<template>
<MenuBrand />
</template>
<script>
import MenuBrand from 'menuBrandingPath/client/components/branding/MenuBrand'; // menuBrandingPath is an alias in the webpack config
export default {
name: 'MenuBar',
components: {
MenuBrand,
},
}
</script>
I've intentionally left out error checking with fs and the fallback path I used to make this example easier to understand. Hopefully it helps someone! :)
You need to use the vue-loader webpack plugin to process the .vue-files in your webpack setup. That enables the () => import(...) syntax.
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
(other loaders....)
]
},
Or, using requireinstead in your original example:
<template>
<b-navbar-brand href="/dashboard">
<MenuBrand />
</b-navbar-brand>
</template>
<script>
import config from '../../../../config.json';
const MenuBrand = require(`../../../../product/${config.product}/components/branding/MenuBrand`);
export default {
name: 'MenuBarBrand',
components: {
'MenuBrand',
},
}
</script>
I am trying to extract SCSS to CSS on my react.js app using webpack plugin: extract-text-webpack-plugin. I am not getting any errors but i can't see any style on my page when i compile. In the following code i am simply trying to change the color of hello world rendered on the screen from black to red. Here are my files:
webpack.config.js
var webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: 'inline-source-map',
entry: [
'webpack-dev-server/client?http://127.0.0.1:8080/',
'webpack/hot/only-dev-server',
'./src/index.js',
'./sass/styles.scss'
],
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
resolve: {
modules: ['node_modules', 'src'],
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['react-hot-loader', 'babel-loader']
},
{ // regular css files
test: /\.css$/,
loader: ExtractTextPlugin.extract({
loader: 'css-loader?importLoaders=1',
}),
},
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new ExtractTextPlugin("styles.css")
]
};
styles.scss
$color : red;
.hello{
font-color: $color;
text-align: center;
}
app.js
import React from 'react';
export default class App extends React.Component {
render() {
return (
<div>
<h1 className = "hello">Hello World</h1>
</div>);
}
}
index.js
import React from 'react';
import { render } from 'react-dom';
import App from './components/app';
render ( <App />, document.getElementById('app'));
What i'm I missing?
you're missing style-loader from your loader chain.
from the sass-loader docs:
Chain the sass-loader with the css-loader and the style-loader to immediately apply all styles to the DOM.
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}]
}]
}
};
you need import styles.scss in code index.js
import 'yourlink/styles.scss'