rollup, styled-components - "withComponent is not a function" error - reactjs

Fairly new to rollup and have come across this problem. I've googled but I can't seem to find an answer but I am fairly sure it is due to the rollup config.
What I am trying to do
I have a React library I created that uses (among others) styled-components, including a number of components that extend other components created using styled-components via .withComponent.
Code example
Badge.js: -
import styled from "styled-components"
const Badge = styled.span`
...
`
export default Badge
Button.js: -
import Badge from "#my/library/Badge"
import styled from "styled-components"
export const Button = styled(Badge.withComponent("button"))`
...
`
rollup.config.js: -
import multiEntry from "rollup-plugin-multi-entry"
import babel from "rollup-plugin-babel"
export default {
input: __dirname + "/src/*.js",
plugins: [
babel({}),
multiEntry()
],
output: {
dir: __dirname + "/lib/",
format: "esm",
},
preserveModules: true,
external: ["#my/library", "styled-components"],
}
babel.config.js: -
module.exports = {
presets: [
'#babel/preset-env',
'#babel/preset-react',
],
plugins: [
"#babel/plugin-proposal-class-properties",
"babel-plugin-styled-components"
],
};
rollup produces the following code: -
Badge.js: -
import { taggedTemplateLiteral as _taggedTemplateLiteral } from './_virtual/_rollupPluginBabelHelpers.js';
import styled from 'styled-components';
function _templateObject() {
var data = _taggedTemplateLiteral([
...
]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
var Badge = styled.span(_templateObject(), function (props) {
...
});
Button.js: -
import styled from 'styled-components';
import Badge from '#my/library/Badge';
var Button = styled(Badge.withComponent("button"))(_templateObject(), function (props) {
...
});
The problem
When running webpack on my app, I get the following error message: -
var Button = (0, _styledComponents["default"])(_Badge["default"].withComponent("button"))(_templateObject(), function (props) {
^
TypeError: _Badge.default.withComponent is not a function
I have no idea why this is happening. I thought that having styled-components listed as an external would resolve this but it hasn't. Again, I am new to rollup. Any help greatly appreciated.

I don't know rollup but you can try with the new syntax of styled-components with prop as
https://www.styled-components.com/docs/api#as-polymorphic-prop
import Badge from "#my/library/Badge"
import styled from "styled-components"
export const Button = styled(Badge).attrs({
as: "button"
})`
...
`
You have to make sure that your component Badge is a styled-components. If not, you could transform it with styled.
https://www.styled-components.com/docs/basics#styling-any-component
if it doesn't help , try to add style-components in globals of output in config rollup
const globals = {
react: 'React',
'styled-components': 'styled',
}

Related

Is there a way to oraginize imports in vs code without removing 'react import'

I am trying to organize imports on saving a file. So I updated vs code settings to always organize imports when saving a file.
But it also removes import React from 'react'.
So react gives me this error 'React' must be in scope when using JSX.
For eg,
import React from 'react'
const Temp = () => {
return (
<div>Temp</div>
)
}
export default Temp
organizes to
const Temp = () => {
return <div>Temp</div>;
};
export default Temp;
This is my react version - "react": "^16.13.1".
Have you tried using a babel.config.js file?
module.exports = {
presets: [
[
'#babel/preset-env',
{
modules: false,
},
],
['#babel/preset-react', { runtime: 'automatic' }],
],
};
I have a project that uses this and it works pretty fine.
Refer the docs for configuration.

React + Rollup - 'r is not defined'

Final edit: Thanks everyone for your help, however ultimately it was easier for me to transition to Webpack and Storybook. I'm leaving my original question untouched just in case it helps anyone in the future. Also, if anyone stumbles upon any issues configuring these (like I did), the link to the GitHub repo is below.
I'm creating a small lib using React and Rollup and trying to test locally with a CRA-powered project, however I'm facing this issue when importing a component from my library. I don't know if the problem is in my configuration or if this is a bug.
Uncaught ReferenceError: r is not defined
at Object.../dist/bundle.js (index.jsx:44)
Imported "Message" component where the error is happening
import React, { useEffect, useState } from 'react';
import { string, number, arrayOf } from 'prop-types';
import Container from './Container';
function Message({
text,
type,
timeout,
classes,
}) {
const [show, setShow] = useState(false);
useEffect(() => {
if (text && type) {
setShow(true);
setTimeout(() => setShow(false), timeout);
}
}, [text, type]);
const date = new Date();
return (
<Container
id={`message-${date}`}
key={`message-${date}`}
className={`${type}${classes?.map((className) => ` ${className}`)}`}
>
{
show
? (
<p>{text}</p>
) : ''
}
</Container>
);
}
// The source map points to this line when the error happens, but it still happens if I remove it and don't use prop-types, instead pointing to the closing bracket of the 'Message' function
Message.defaultProps = {
timeout: 3000,
classes: [],
};
Message.propTypes = {
text: string.isRequired,
type: string.isRequired,
timeout: number,
classes: arrayOf(string),
};
export default Message;
Test component where it's being used:
import React from 'react';
import { Message } from 'pure-ui';
import { getRandomArrayElement } from 'formatadores';
const types = [
'warning',
'error',
'success',
];
const texts = [
'This is a test',
'I will randomly display a message every so often, so stay sharp',
'Yet another test message',
];
const timeouts = [
5000,
3000,
1000,
];
function App() {
return (
<div>
<h1>Running...</h1>
<Message
type={getRandomArrayElement(types)}
text={getRandomArrayElement(texts)}
timeout={getRandomArrayElement(timeouts)}
/>
</div>
);
}
export default App;
rollup config file:
import babel from '#rollup/plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import React from 'react';
import propTypes from 'prop-types';
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
export default [
{
input: 'src/index.js',
watch: true,
output: {
file: 'dist/bundle.js',
format: 'iife',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'prop-types': 'PropTypes',
},
},
plugins: [
external(),
babel({
exclude: 'node_modules/**',
presets: [
'#babel/preset-env',
['#babel/preset-react', { runtime: 'automatic' }],
],
}),
resolve({ extensions }),
commonjs({
namedExports: {
react: Object.keys(React),
'react/jsx-runtime': ['jsx', 'jsxs', 'Fragment'],
'react/jsx-dev-runtime': ['jsx', 'jsxs', 'jsxDEV'],
'prop-types': Object.keys(propTypes),
},
}),
],
external: [
'react',
'react-dom',
'prop-types',
],
},
];
I tried changing the namedExports (and also removing them), linking React from the lib to use the same version from the CRA project (in the lib both React and React DOM are listed as peer dependencies), but I always end with the same result. Is there something wrong with my config? This is the first time I use Rollup for creating a React component lib, so maybe there's something I missed
If the above info is insufficient, here's the GitHub repo
Thanks in advance
Edit: I just saw that I forgot to import React in my test project, however after doing so the results were the same, editing my original question just to fix that.
Update 1: I changed several configurations (changed deprecated rollup-plugins to their currently maintained versions, added globals to the output part of rollup.config.js, added namedExports to commonjs plugin configuration, added an external section specifying react, react-dom and prop-types), but now what I'm getting is a React is not defined error, updating the question with the new config

Rollup plugin postcss does not inject css imported from node_modules

Here is my rollup config
// rollup.config.js
const postcss = require('rollup-plugin-postcss');
const autoprefixer = require('autoprefixer');
module.exports = {
rollup(config, _options) {
config.plugins.push(
postcss({
plugins: [
autoprefixer(),
],
extensions: ['.css'],
modules: false,
extract: false,
}),
);
return config;
},
};
So if I import css file local from a relative path, it gets injected but I import from node_modules it doesn't
import React from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
// The following works if I copy the file locally
// import './ReactToastify.css';
What am I doing wrong here?
I came across exactly the same problem and I think I found the solution. The trick here is to use rollup-plugin-postcss (or also rollup-plugin-styles) in combination with #rollup/plugin-node-resolve.
My rollup.config.js looks something like this:
import { nodeResolve } from '#rollup/plugin-node-resolve'
import postcss from 'rollup-plugin-postcss'
// import styles from 'rollup-plugin-styles'
export default {
...
plugins: [
postcss(),
// styles(),
nodeResolve({
extensions: ['.css']
})
]
};
As far as I can tell, for my simple case, it doesn't matter if you use postcss or styles plugins. Both work the same.

ESLint: '#material-ui/core/Button' import is restricted from being used

I'm writing a React 16 application, I'm using material-ui components.
I import Button like this:
import Button from '#material-ui/core/Button';
just like it says in the official documentation: https://material-ui.com/components/buttons/
WebStorm show an error saying:
ESLint: '#material-ui/core/Button' import is restricted from being used. This loads CommonJS version of the package. To fix replace with: import { Button } from "#material-ui/core";(no-restricted-imports)
When I change the import to:
import {Button} from '#material-ui/core';
the message error goes away. The code works in both instances.
I want to know why I need to change the import for the error to disappear. why can't I just use the same code suggested by material-ui.com itself.
The template I'm using uses this .eslintrc.js
"use strict";
const fs = require("fs");
const path = require("path");
const restrictedPaths = [
{ name: "react-bootstrap" },
{ name: "#material-ui/core" }
].map(pkg =>
fs
.readdirSync(path.dirname(require.resolve(`${pkg.name}/package.json`)))
.map(component => ({
name: `${pkg.name}/${component}`,
message: `This loads CommonJS version of the package. To fix replace with: import { ${component} } from "${pkg.name}";`
}))
);
// TODO: Wait for https://github.com/facebook/create-react-app/pull/7036 to enable rules in react-scripts.
module.exports = {
extends: "eslint-config-react-app",
rules: {
// "no-script-url": "warn",
"jsx-a11y/anchor-is-valid": "warn",
"no-restricted-imports": ["error", { paths: [].concat(...restrictedPaths) }]
}
};
When you specify the full path to the file you want to import, then you import this file. When you use the import {Button} from '#material-ui/core', you let node choose the file it wants to use for this import. The package.json of a module may specify different entry points for a single #material-ui/core, depending on if working with CommonJS or ES modules.

Proper way to implement jwplayer in react component using webpack (react-starter-kit)

i am making VideoPlayer react component with jwpalyer and i am using webpack es6 for loading module
webpack support npm module loading & there is no npm for jwplayer
so am trying to include jwplayer.js using es6 import but it giving me error
ReferenceError: window is not defined
so any one can help me to properly setup jwplayer with webpack
import React, { PropTypes, Component } from 'react';
import $ from 'jquery';
import Player from "./lib/jwplayer/jwplayer.js";
import styles from './VideoPayer.css';
import withStyles from '../../decorators/withStyles';
import Link from '../Link';
#withStyles(styles)
class VideoPlayer extends Component {
static propTypes = {
className: PropTypes.string,
};
static defaultProps = {
file: '',
image: ''
};
constructor(props) {
super(props);
this.playerElement = document.getElementById('my-player');
}
componentDidMount() {
if(this.props.file) {
this.setupPlayer();
}
}
componentDidUpdate() {
if(this.props.file) {
this.setupPlayer();
}
}
componentWillUnmount() {
Player().remove(this.playerElement);
}
setupPlayer() {
if(Player(this.playerElement)) {
Player(this.playerElement).remove();
}
Player(this.playerElement).setup({
flashplayer: require('./lib/player/jwplayer.flash.swf'),
file: this.props.file,
image: this.props.image,
width: '100%',
height: '100%',
});
}
render() {
return (
<div>
<div id="my-player" className="video-player"></div>
</div>
)
}
}
export default VideoPlayer;
I think this is what you need to do:
Define window as external to the bundle so that references to it in other libraries are not mangled.
Expose a global variable jwplayer so that you can attach your key
(Optional) Create an alias to your jwplayer library
I've tested it and this configuration works for me, but only on the client and not on the server or isomorphically/universally.
webpack.config.js:
// Declare window as external
externals: {
'window': 'Window'
},
// Create an easy binding so we can just import or require 'jwplayer'
resolve: {
alias: {
'jwplayer':'../path/to/jwplayer.js'
}
},
// Expose jwplayer as a global variable so we can attach the key, etc.
module: {
loaders: [
{ test: /jwplayer.js$/, loader: 'expose?jwplayer' }
]
}
Then you can import jwplayer from 'jwplayer' and require('jwplayer').
Probably an old question but I recently found a relatively stable solution.
I include the jwplayer in a folder called app/thirdparty/jwplayer-7.7.4. Next, add it to the exclude in the babel loader so it is not parsed.
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /(node_modules|thirdparty)/,
}
I then use dynamic import in order to bootstrap my component and load jwplayer.
async function bootstrap(Component: React.Element<*>) {
const target = document.getElementById('root');
const { render } = await import('react-dom');
render(<Component />, target);
}
Promise.all([
import('app/components/Root'),
import('app/thirdparty/jwplayer-7.7.4/jwplayer.js'),
]).then(([ { default: Root } ]) => {
window.jwplayer.key = "<your key>";
bootstrap(Root);
});

Resources