Rollup + React 17 with new JSX Transform - "React is not defined" - javascript

I'm trying to prototype a microfrontend architecture with Rollup and a couple of create-react-app applications. However when I locally yarn link my external app with the container app, I run into the following error:
ReferenceError: React is not defined
23500 | return /#PURE/React.createElement("div", {
| ^ 23501 | id: "container",
23502 | className: "flex flex-col h-screen"
23503 | }, /#PURE/React.createElement(BrowserRouter, null, /#PURE/React.createElement(Header, {
I think it's because we're not importing React at the top of every component/file because of React 17's new JSX Transform allowing you to not have to do that. I'd really like to be able to build our micro frontend package without having to import React in every file, is there a way to do this?
Here is the rollup.config.js:
import babel from 'rollup-plugin-babel';
import commonjs from '#rollup/plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import resolve from '#rollup/plugin-node-resolve';
import image from '#rollup/plugin-image';
import visualizer from 'rollup-plugin-visualizer';
import includePaths from 'rollup-plugin-includepaths';
import replace from '#rollup/plugin-replace';
import pkg from './package.json';
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
export default {
input: './src/App.jsx',
output: [
{
file: pkg.main,
format: 'cjs',
},
{
file: pkg.module,
format: 'esm',
},
],
plugins: [
external(),
postcss(),
resolve({
mainFields: ['module', 'main', 'jsnext:main', 'browser'],
extensions,
}),
image(),
visualizer(),
includePaths({ paths: ['./'] }),
replace({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
babel({
exclude: 'node_modules/**',
plugins: [
[
'module-resolver',
{
root: ['src'],
},
],
],
presets: ['#babel/preset-react'],
}),
commonjs(),
],
};

In tsconfig.json, add the following code
{
"compilerOptions": {
"jsx": "react-jsx",
}
}

Fixed this by adding { runtime: "automatic" } to the #babel/preset-react preset.
From the preset-react runtime docs:
automatic auto imports the functions that JSX transpiles to. classic does not automatic import anything.
Also mentioned in the React post about the new JSX transform:
Currently, the old transform {"runtime": "classic"} is the default option. To enable the new transform, you can pass {"runtime": "automatic"} as an option to #babel/plugin-transform-react-jsx or #babel/preset-react
Here's a sample:
{
// ...
plugins: [
// ...
babel({
// ...
presets: [
// ...
["#babel/preset-react", { runtime: "automatic" }],
]
})
]
}

Related

RollupJS: [Component] is undefined error in jsx/tsx components

I have a simple react + typescript project that is half typescript, another written in commonJS that is using rollup for bundling. My rollup.config is like below
import resolve from '#rollup/plugin-node-resolve'
import babel from '#rollup/plugin-babel'
import postcss from 'rollup-plugin-postcss'
import typescript from '#rollup/plugin-typescript'
import commonjs from '#rollup/plugin-commonjs'
export default {
input: 'src/index.js',
output: {
file: 'lib/bundle.js',
format: 'umd',
name: '[name]',
},
plugins: [
resolve(),
postcss({ plugins: [], writeDefinitions: true, modules: true }),
typescript({ outputToFilesystem: true, jsx: 'preserve' }),
babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),
commonjs(),
],
external: [
'react',
'react-sizeme',
'lodash',
],
}
Things were working fine, untill I decided to add few jsx/tsx components. I started getting plugin error after using jsx/tsx. I learnt that #rollup/plugin-typescript can't reserve jsx/tsx from here . I updated my config and added the acorn-jsxplugin .
import resolve from '#rollup/plugin-node-resolve'
import babel from '#rollup/plugin-babel'
import postcss from 'rollup-plugin-postcss'
import typescript from '#rollup/plugin-typescript'
import commonjs from '#rollup/plugin-commonjs'
import jsx from 'acorn-jsx'
export default {
input: 'src/index.js',
output: {
file: 'lib/bundle.js',
format: 'umd',
name: '[name]',
},
acornInjectPlugins: [jsx()],
plugins: [
resolve(),
postcss({ plugins: [], writeDefinitions: true, modules: true }),
typescript({ outputToFilesystem: true, jsx: 'preserve' }),
babel({ babelHelpers: 'bundled', exclude: 'node_modules/**' }),
commonjs(),
],
external: [
'react',
'react-sizeme',
'lodash',
],
}
While It resolved the error, My final build doesn't include the imported jsxt/tsx components. example
const ExternalComp: React.FC<{ name: string }> = ({ name: string }) => React.reactElement<void> => {
return (<div> I am external {name}</div>
}
const MyFunction: React.FC<{ name: string }> = ({ name: string}) => React.reactElement<void> => {
return(
<ExternalComp name={name} />
)
}
When I bundle my build above, ExternalComp seems to be omitted all the time and is throwing undefined error all the time in my app. acorn-jsx seem to be omitting the components outside the main component from the final bundle/build. Please what causes this and how do I address it ? Any help will be appreciated.
Update
Adding console.log([ExternalComp]) inside MyFunction forces RollUp to bundle with ExternalComp . Just rendering it <ExternalComp /> doesn't.

Webpack bundled library does not compile in React

I'm trying to bundle a standalone library with Webpack (v5) and use it in my React application.
The webpack.config.js file is pretty straightforward:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './TestWebpack.js',
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
output: {
filename: 'widgets.bundle.js',
path: path.join(__dirname, '..', 'ClientApp', 'src', 'dynamicBundles'),
library: {
name: 'widgets',
type: 'umd',
umdNamedDefine: true,
}
},
externals: {
'react': {
'commonjs': 'react',
'commonjs2': 'react',
'amd': 'react',
'umd': 'react'
},
'react-dom': {
'commonjs': 'react-dom',
'commonjs2': 'react-dom',
'amd': 'react-dom',
'umd': 'react-dom'
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'#babel/preset-react'
],
}
}
},
]
},
};
The TestWebpack.js file is also very simple:
import React from 'react';
export default function TestWebpack(){
return (
<div>Test</div>
)
}
But when I compile it with Webpack and use it in my React application like this:
import React from 'react'
import { useEffect } from 'react'
import * as Widgets from '../dynamicBundles/widgets.bundle'
export default function TestUsingWebpack(){
useEffect(() => {
console.log('Object.keys(Widgets) - ', Object.keys(Widgets));
}, []);
return <div>Test</div>
}
I get the following errors:
Line 12: 'define' is not defined no-undef
Line 13: 'define' is not defined no-undef
Line 18: Unexpected use of 'self' no-restricted-globals
I can't figure out how to solve this. I see in the bundled file that in these lines an undefined variable 'define' is used. I saw in other answers that people put lines at the start to define it properly, but this bundle may change frequently so it's not an answer.
Maybe there's something wrong with my Webpack config file? I'm new to this and tried following the newest articles and Webpack documentation about standalone library but it's still not working.

How to configure Relay.JS in Vite?

I'm trying to migrate my React project from CRA to Vite, this is my vite.config.js:
import { defineConfig } from 'vite'
import react from '#vitejs/plugin-react'
import envCompatible from 'vite-plugin-env-compatible'
import relay from "vite-plugin-relay"
import macrosPlugin from "vite-plugin-babel-macros"
import path from 'path';
import fs from 'fs/promises';
export default defineConfig({
resolve: {
alias: {
'~': path.resolve(__dirname, 'src'),
'#material-ui/core': path.resolve(__dirname, 'node_modules/#material-ui/core')
}
},
esbuild: {
loader: "tsx",
include: /src\/.*\.[tj]sx?$/,
exclude: [],
},
optimizeDeps: {
esbuildOptions: {
plugins: [
{
name: "load-js-files-as-jsx",
setup(build) {
build.onLoad({ filter: /src\/.*\.js$/ }, async (args) => ({
loader: "tsx",
contents: await fs.readFile(args.path, "utf8"),
}));
},
},
],
},
},
define: {
global: {},
},
plugins: [
envCompatible(),
react(),
relay,
//macrosPlugin(),
],
})
My GraphQL query files are like this:
import graphql from 'babel-plugin-relay/macro'
const getQuery = () => graphql`
query UserQuery($id: ID!) {
user(id: $id) {
id
fullName
}
}
`
export default getQuery
When I tried to run the project in dev mode (command $ vite), I got this error:
So I did some search and replaced vite-plugin-relay to vite-plugin-babel-macros like this:
// others import
import relay from "vite-plugin-relay"
import macrosPlugin from "vite-plugin-babel-macros"
export default defineConfig({
// configs like bellow
plugins: [
envCompatible(),
react(),
//relay,
macrosPlugin(),
],
})
So I started to get a new error:
How can I configure Relay to work on Vite.JS?
Might be a bit late, but the issue has been fixed in Relay#13 and you can find some workarounds in this thread for older versions of Relay :
https://github.com/facebook/relay/issues/3354
You can also try adding the option eagerEsModules: true to your relay babel plugin configuration.
Unless you have some specific usecase that requires the use of babel-plugin-relay, your issue should be resolved if you change your imports from
import graphql from 'babel-plugin-relay/macro'
to
import { graphql } from "react-relay";
You should only need the relay vite plugin at that point, and can remove vite-plugin-babel-macros
There's a few things wrong with your setup.
1. Don't use vite-plugin-babel-macros:
Use #vitejs/plugin-react instead.
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
import relay from "vite-plugin-relay";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [relay, react({
babel: {
plugins: ['babel-plugin-macros']
},
})],
});
You can probably get it to work with vite-plugin-babel-macros, but the later is an official plugin.
2. Don't use 'babel-plugin-relay/macro':
Use the following instead:
import { graphql } from "react-relay";
It's unclear to me why the official docs suggest using babel-plugin-relay/macro, but that just doesn't work.
3. Configure relay.config.js correctly:
{
"src": "./src",
"language": "typescript",
// Change this to the location of your graphql schema
"schema": "./src/graphql/schema.graphql",
"exclude": [
"**/node_modules/**",
"**/__mocks__/**",
"**/__generated__/**"
],
"eagerEsModules": true
}
In particular, make sure you use language: typescript and eagerEsModules.
4. Sample repository
I wrote a sample repository showing how to properly configure React Relay with Vite.js and TypeScript, you can find it here.

Rollup imported css in a webcomponent

I am creating simple webcomponent now I want to import css , I found a method using adpotedstylesheet.
Here is how I import it my webcomponet
import sheet from './styles/swal.css' assert { type: 'css' };
class Demo extends HTMLElement{
constructor() {
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(Demo.content.cloneNode(true));
document.adoptedStyleSheets = [sheet];
this.shadowRoot.adoptedStyleSheets = [sheet];
}
}
window.customElements.define("demo-component", Demo);
and here is my rollup settup for bundling my component
import summary from "rollup-plugin-summary";
import { terser } from "rollup-plugin-terser";
import resolve from "#rollup/plugin-node-resolve";
import replace from "#rollup/plugin-replace";
import postcss from "rollup-plugin-postcss";
import { eslint } from "rollup-plugin-eslint";
import babel from "rollup-plugin-babel";
import { uglify } from "rollup-plugin-uglify";
import commonjs from 'rollup-plugin-commonjs';
export default {
input: "index.js",
output: {
file: "dist/index.js",
format: "esm",
},
onwarn(warning) {
if (warning.code !== "THIS_IS_UNDEFINED") {
console.error(`(!) ${warning.message}`);
}
},
plugins: [
postcss({
plugins: [],
extensions: [".css"],
}),
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
eslint({
exclude: ["src/styles/**"],
}),
babel({
exclude: "node_modules/**",
}),
terser({
ecma: 2017,
module: true,
warnings: true,
mangle: {
properties: {
regex: /^__/,
},
},
}),
summary(),
replace({
"Reflect.decorate": "undefined",
preventAssignment: true,
ENV: JSON.stringify(process.env.NODE_ENV || "development"),
}),
process.env.NODE_ENV === "production" && uglify(),
],
};
Now when i run npm run buil I get the following error.
[!] (plugin commonjs) SyntaxError: Unexpected token (3:38)`
What am I doing wrong here ???
Currenly, Rollup doesn't support import assertions. There is open issue for Rollup to address it. There is an experimental Rollup plugin that supports this but it seems to have some issues.
Another option you can try is to use rollup-string-plugin. You can use it to read CSS file as a string and then build your constructible stylesheets and assign it to adoptedStyleSheets property as explained here for Webpack. Following is one example of doing it..
// Read SCSS file as a raw CSS text
import styleText from './my-component.css';
const sheet = new CSSStyleSheet();
sheet.replaceSync(styleText);
// Custom Web component
class FancyComponent1 extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
// Attaching the style sheet to the Shadow DOM of this component
shadowRoot.adoptedStyleSheets = [sheet];
shadowRoot.innerHTML = `
<div>
<p>Hello World</p>
</div>
`;
}
}
Side note: With Webpack, you can use raw-loader.
Be aware that adoptedStyleSheets is currently not supported by Safari on Mac and iOS. But, Rollup might handle this for you - I don't know.
Another solution is to check out:
https://www.npmjs.com/package/csshtml-module
This CLI tool can be set up to let you automatically compile CSS/HTML to native JS modules.
I created that CLI tool to tackle this issue. It might not be for everyone - but maybe it resonate with you.

Rollup Error: 'default' is not exported by node_modules/react/index.js

I have a project where I use webpack and want to switch to rollup.js but I have trouble regarding the plugin #rollup/plugin-commonjs.
My rollup.conf.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import nodePolyfills from 'rollup-plugin-node-polyfills';
const config = {
input: 'site/templates/scripts/master.js',
output: [
{
file: 'site/templates/scripts/master.min.js',
format: 'cjs'
}
],
plugins: [
nodePolyfills(),
resolve({
browser: true
}),
commonjs({
include: /node_modules/,
namedExports: {
'react': ["useState", "useEffect"],
'#apollo/client': ['ApolloProvider', 'ApolloClient', 'HttpLink', 'InMemoryCache', 'useQuery', 'gql'],
'styled-components': [ 'styled', 'css', 'ThemeProvider' ]
}
}),
babel({
babelrc: true,
exclude: 'node_modules/**'
}),
terser()
]
};
export default config;
The Error I'm getting and don't know to solve
site/templates/scripts/master.js → site/templates/scripts/master.min.js...
[!] Error: 'default' is not exported by node_modules/react/index.js, imported by site/templates/scripts/src/BgProductRecommendations/FredhopperProduct.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
site/templates/scripts/src/BgProductRecommendations/FredhopperProduct.js (3:7)
1: 'use strict';
2:
3: import React from "react";
^
4:
5: const FredhopperProduct = ({
Error: 'default' is not exported by node_modules/react/index.js, imported by site/templates/scripts/src/BgProductRecommendations/FredhopperProduct.js
at error (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:10152:30)
at Module.error (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:14487:16)
at handleMissingExport (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:14388:28)
at Module.traceVariable (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:14871:24)
at ModuleScope.findVariable (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:13448:39)
at FunctionScope.findVariable (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:8661:38)
at ChildScope.findVariable (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:8661:38)
at MemberExpression.bind (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:11285:49)
at CallExpression$1.bind (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:8746:23)
at CallExpression$1.bind (/usr/local/lib/node_modules/rollup/dist/shared/rollup.js:11473:15)
The https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module page does not really help, since I have all named exports which I use in my config.
Finally I found what was wrong. I needed the #rollup/plugin-replaceplugin to replace process.env.NODE_ENV
Here's the working code
It needed also some more namedExports.
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import nodePolyfills from 'rollup-plugin-node-polyfills';
import replace from '#rollup/plugin-replace';
import React from 'react';
import ReactIs from 'react-is';
import ReactDOM from 'react-dom';
const config = {
input: 'site/templates/scripts/master.js',
output: [
{
file: 'site/templates/scripts/master.min.js',
format: 'cjs'
}
],
plugins: [
replace({
"process.env.NODE_ENV": JSON.stringify("development")
}),
nodePolyfills(),
resolve({
browser: true
}),
commonjs({
include: /node_modules/,
namedExports: {
'react-is': Object.keys(ReactIs),
'react': Object.keys(React),
'react-dom': Object.keys(ReactDOM),
'#apollo/client': ['ApolloProvider', 'ApolloClient', 'HttpLink', 'InMemoryCache', 'useQuery', 'gql'],
'styled-components': [ 'styled', 'css', 'ThemeProvider' ]
}
}),
babel({
babelrc: true,
exclude: 'node_modules/**'
}),
terser()
]
};
export default config;
As of the latest #rollup/plugin-commonjs version, namedExports are handled by default.
Also try using this rollup babel config, requires the #babel/preset-react module to be installed
babel({
exclude: "node_modules/**",
presets: ["#babel/preset-react"],
babelHelpers: "bundled",
}),
And since the date of the issue most rollup plugins have been namespaced so try to reinstall #rollup/plugin-babel
The solution for this problem is to update commonjs option to make it as below
import commonjs from '#rollup/plugin-commonjs';
// rollup.config.js
export default {
input: 'src/app.ts',
output: [
{
...
commonjs({
include: /node_modules/,
requireReturnsDefault: 'auto', // <---- this solves default issue
}),
documentation link:
https://www.npmjs.com/package/#rollup/plugin-commonjs
search for requireReturnsDefault

Categories

Resources