Unable to expose a component library in react with webpack and babel - javascript

I'm creating a small component library for react. Something that can be required like var Components = require('components'). This will have individual components, much like react-bootstrap does. I'm trying to use webpack with babel to compile this into an index.js file. The compilation went well. I published this to my local npm registry and installed it in one of my other projects. When I require it - require('components') - the require returns an empty object. Below is my folder structure
root
|- components
| |- ImageEditor.js
|
|- lib
| |- index.compiled.js (file compiled by webpack)
|
|- index.js (requires ./components/ImageEditor.js, entry point for webpack)
|- webpack.config.js
ImageEditor.js
import React from 'react';
import ReactDOM from 'react-dom';
import Canvas from './utils/canvas';
import '../stylesheets/imageeditor.scss';
class ImageManipulation extends React.Component {
static propTypes = {};
state = {
height: 200,
width: 200,
scale: 1.25
};
static defaultProps = {
color: [213, 214, 217, 0.6],
image: ""
};
...
render() {
return (
<div className="_react-image-manipulation">
<div className="_cover-box">
{ this.loadComponent() }
</div>
</div>
);
}
}
export default ImageManipulation;
index.js
import ImageEditor from './components/ImageEditor';
export default {
ImageEditor
};
webpack.config.js
var path = require('path');
var NODE_ENV = process.env.NODE_ENV || 'development';
var WebpackNotifierPlugin = require('webpack-notifier');
var UglifyJSPlugin = require("webpack/lib/optimize/UglifyJsPlugin");
var CleanWebpackPlugin = require("clean-webpack-plugin");
var commonPlugins = [
new WebpackNotifierPlugin({
title: 'Contour Components'
})
];
function getPlugins() {
if (NODE_ENV == 'prod') {
commonPlugins.push(new CleanWebpackPlugin(['lib/*']));
commonPlugins.push(new UglifyJSPlugin({
compress: {
warnings: false
}
}));
}
return commonPlugins;
}
module.exports = {
devtool: 'sourcemap',
entry: {
index: './index.js'
},
output: {
path: path.join(__dirname, 'public'),
filename: '[name].compiled.js'
},
plugins: getPlugins(),
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
query: {
cacheDirectory: true,
presets: ['es2015', 'stage-0', 'react'],
plugins: ['add-module-exports', "transform-class-properties"]
},
exclude: /node_modules/
}, {
test: /\.json$/,
loader: 'json-loader'
}, {
test: /\.png$/,
loader: "url-loader?limit=100000&mimetype=image/png"
}, {
test: /(\.scss|\.css)$/,
include: /components/,
loader: 'style!css!sass'
}]
},
resolve: {
extensions: ['', '.scss', '.js', '.json', '.png'],
modulesDirectories: [
'node_modules',
'components'
]
}
};
Any idea what I'm doing wrong here?

You are exporting component in the object in the default export.
Babel 6 produces the following CommonJS module for you. See REPL:
exports.default = {
ImageEditor: ImageEditor
};
Then you can use this component like this:
var ImageEditor = require('my-lib').default.ImageEditor
Your component is hidden under the default key. If you don't want it, use named exports instead.
export {ImageEditor};
For this, Babel produces the following code
exports.ImageEditor = ImageEditor;
Look, no extra default key, and everything work as expected

Related

Vue plugin component with own CSS (style is extracted but not applied)

I am making a Vue plugin that contains SFC with its own styles. I am using webpack4 to bundle. For production I use MiniCssExtractPlugin to extract CSS from SFC to separate file. Everything builds up correctly, style.css is created in dist folder and my plugin is working but seems like css from file is not loaded. Is there a step which I am missing?
Thank you
main.js
import SearchBar from "./components/Searchbar.vue";
const defaultOptions = {};
export let Search = {
install(Vue, options) {
let userOptions = { ...defaultOptions, ...options };
let props = { ...SearchBar.props };
Object.keys(userOptions).forEach(k => {
props[k] = { default: userOptions[k] };
});
Vue.component("SearchBarComponent", { ...SearchBar, props });
}
};
Searchbar.vue
<template>
<div class="search">My search template</div>
</template>
<script>
export default{
}
</script>
<style>
.search{
background-color:blue;
}
</style>
webpack.config.js
let path = require("path");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
libraryTarget: "commonjs2",
publicPath: "/dist/",
filename: "search.min.js"
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "style.css"
})
],
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
name: "[name].[ext]?[hash]"
}
},
{
test: /\.css$/,
use: [
process.env.NODE_ENV !== "production"
? "vue-style-loader"
: MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
};
In my project, I just install my plugin from npm repository then I use
import {Search} from "my-search-plugin";
Vue.use(Search);
to import plugin, and then call
<SearchBarComponent>
wherever I need. The component appear successfully but styles and not applied.

How to remove react from build from custom component?

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"));
// ...

webpack inline-source-map is mapping to the wrong file

I would prefer to use inline source maps in my webpack config as it sources to the actual original file.
my webpack config is...
const paths = {
src: './widgets/src/scripts/index.js',
dest: '/src/main/content/jcr_root/etc/designs/universal-template-abloyalty/clientlib/js'
};
module.exports = (env, argv) => {
return {
devtool: argv.mode === 'production' ? 'source-map' : 'inline-source-map',
entry: paths.src,
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
}
]
},
output: {
path: __dirname + paths.dest,
filename: 'widgets.js'
},
resolve: {
extensions: ['*', '.js', '.jsx']
}
};
};
and then for example in my index.js i do a simple console log...
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
initReact = () => {
function createRootElement() {
const el = document.createElement('div');
el.id = 'root';
document.body.appendChild(el);
return el;
}
const root = document.getElementById('root') || createRootElement();
ReactDOM.render(<App />, root);
console.log('test');
};
if (typeof onReactReady === 'function') onReactReady();
The problem I am having is that in my browser console, this should obviously map to index.js:16 using inline-source-map. Instead its mapping to MobileFilterComponent.js:15
Any thoughts on why this could be happening?
Interestingly enough when I use eval-source-map it maps correctly, but with an altered file name. This makes looking up a file from ctrl+p in sources a pain as you may not know how the file is named.
**note I tagged this with AEM, as this is for an AEM project. I am wondering if maybe when AEM builds clientlib.js if some how the source maps are getting corrupted.

How to use React.js with AdonisJs

How can I use React.js in the front-end of my AdonisJs project?
I've tried to install react with npm install react I thought this will works correctly. I made a file app.js with this code:
var React = require('react');
var ReactDOM = require('react-dom');
export default class Index extends Component {
render() {
return (< h1 > hello world! < /h1>)
}
}
ReactDOM.render( < Index / > , document.getElementById('example'))
But this isn't working at all, I don't know what to do more I have searched about how to use React in Adonis but I didn't find anything interesting.
I strongly suggest you to use WebPack for this task.
create a webpack.config.js file in your root directory:
maybe your code need some changes but this is the idea:
const webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var path = require('path');
var pkgBower = require('./package.json');
module.exports = {
target: "web",
devtool: "source-map",
node: {
fs: "empty"
},
entry: {
'app': path.join(__dirname, 'react-app', 'Index.jsx')
},
resolve: {
modules: [__dirname, 'node_modules', 'bower_components'],
extensions: ['*','.js','.jsx', '.es6.js']
},
output: {
path: path.join(__dirname, 'public', 'src'),
filename: '[name].js'
},
resolveLoader: {
moduleExtensions: ["-loader"]
},
module: {
loaders: [
{
test: /jquery\.flot\.resize\.js$/,
loader: 'imports?this=>window'
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react', 'stage-0'],
compact: false
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.woff|\.woff2|\.svg|.eot|\.ttf/,
loader: 'url?prefix=font/&limit=10000'
},
{
test: /\.(png|jpg|gif)$/,
loader: 'url?limit=10000'
},
{
test: /\.scss$/,
loader: 'style!css!sass?outputStyle=expanded'
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
new CopyWebpackPlugin([{
from: 'img',
to: 'img',
context: path.join(__dirname, 'react-app')
}, {
from: 'server',
to: 'server',
context: path.join(__dirname, 'react-app')
}, {
from: 'fonts',
to: 'fonts',
context: path.join(__dirname, 'react-app')
}]),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false },
mangle: true,
sourcemap: false,
beautify: false,
dead_code: true
}),
new webpack.ContextReplacementPlugin(/\.\/locale$/, 'empty-module', false, /js$/)
]
};
Then put your app in your root directory, in a folder react-app/ and your components can go inside that folder:
For example your file: [Index.jsx]
import React from 'react';
import ReactDOM from 'react-dom';
class Index extends Component {
render() {
return (< h1 > hello world! < /h1>)
}
}
ReactDOM.render( < Index / > , document.getElementById('example'));
you don't need to export this Index component in this case, but in case you need to just use export default ComponentName at the end of your component file.
The next step is in your routes file (I'm using AdonisJS 4) but is very similar for the version 3.x
Route.any("*", ({ view, response }) => (
view.render('index')
))
Then your index (.njk, or .edge) file (under resources/views) should look something like this:
<html>
<head>
<link rel="stylesheet" href="/src/styles.css">
<script type="text/javascript" src="/src/app.js"></script>
</head>
<body>
<div id="example"></div>
</body>
</html>
--
You need to install some npm/ packages with npm or yarn,
and remember to run in another terminal window or tab,
webpack -d --watch --progress
Regards.
This works. You are missing React.Component.
var React = require('react');
var ReactDOM = require('react-dom');
export default class Index extends React.Component {
render() {
return (< h1 > hello world! < /h1>)
}
}
ReactDOM.render( < Index / > , document.getElementById('example'))
Alternatively you can use import React, {Component} from 'react'; if you want to use your code structure.
For decoupled use, just build your React app and copy the build to Adonis public folder.

Transpile to ES5 including files outside root directory

I have a project with multiple apps and some common react components in this structure:
ui
- app1
- app2
- components
The javascript files in components are written in ES6. Both app1 and app2 have this webpack config:
var path = require('path')
module.exports = {
entry: ['babel-polyfill', './src/index.js'],
module: {
rules: [{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, '../components'),
path.resolve(__dirname, 'src')
],
loader: 'babel-loader'
}]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
alias: {
InputComponents: path.resolve(__dirname, '../components'),
}
},
}
Babel config:
{
"plugins": [
"transform-async-to-generator",
"glamor/babel"
],
"presets": [
"es2015",
"react",
"stage-0"
]
}
My code:
import React, { Component } from 'react'
import { Button } from 'CommonComponents'
export default class ButtonView extends Component {
render () {
return <Button>GO!</Button>
}
}
I want my components and my apps to be built and transpiled to ES5 using Babel. The apps work but not the external components and I get errors like:
Module build failed: SyntaxError: Unexpected token (6:34)
4 | class Button extends Component {
5 | render () {
> 6 | if (this.props.stroke) return <ButtonStroke {...this.props} />
| ^
How can I make sure that babel transpiles my external components?

Categories