how to prevent multiple copies of React when developing library with Rollup? - javascript

I am developing tiny ui react library. I am using Rollup as a bundler. and i faced some strange issue:
react.development.js:1476 Uncaught Error: Invalid hook call. Hooks can only be called
inside of the body of a function component. This could happen for one of the
following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Here is my Rollup config:
import babel from "rollup-plugin-babel";
import resolve from "#rollup/plugin-node-resolve";
import external from "rollup-plugin-peer-deps-external";
import { terser } from "rollup-plugin-terser";
import postcss from "rollup-plugin-postcss";
import typescript from "rollup-plugin-typescript2";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
const packageJson = require("./package.json");
export default [
{
input: ["./src/index.ts"],
output: [
{
file: packageJson.main,
format: "cjs",
sourcemap: true,
},
{
file: packageJson.module,
format: "esm",
sourcemap: true,
},
],
globals: {
react: "React",
"react-dom": "ReactDOM",
},
external: ["react", "react-dom"],
plugins: [
peerDepsExternal({ includeDependencies: false }),
postcss({
plugins: [],
minimize: true,
}),
babel({
exclude: "node_modules/**",
presets: ["#babel/preset-react"],
}),
external(),
resolve(),
typescript({ useTsconfigDeclarationDir: true }),
terser(),
],
},
];
Component itself if very simple. Nothing special so i am skipping its code.
When i am publishing my lib to NPM - everything is working exactly as expected.
But when i am doing local instal with
npm i ../my-local-lib
I have this error in console:
react.development.js:1476 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
My expectation was that peerDepsExternal plugin will handle this case, but obviously i have messed up somewhere.

Related

Attempted import error: 'useInsertionEffect' is not exported from 'react' [duplicate]

1: https://i.stack.imgur.com/Y2Zdo.png**strong text**
here i am trying to use Appbar component of material ui but i get this error
npm install #mui/material#5.4.2 #emotion/react#11.7.1 #emotion/styled#11.6.0
solves everything...
This was a bug that was caused by useInsertionEffect being referenced directly in the specifiers list of the import statement (React.useInsertionEffect instead of React['useInsertion' + 'Effect']).
It is fixed as of #emotion/react#11.8.1 - upgrade to that version, and the error should disappear.
I got a similar error, but not with Appbar. I was trying to create my own npm component library with mui v5 components.
ERROR in ./node_modules/comp-lib-mui5-sample/dist/esm/index.js
1293:9-29
export 'useInsertionEffect' (imported as 'e') was not found in 'react'
(possible exports: Children, Component, Fragment, Profiler,
PureComponent, StrictMode, Suspense,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, cloneElement, createContext, createElement, createFactory, createRef, forwardRef,
isValidElement, lazy, memo, useCallback, useContext, useDebugValue,
useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer,
useRef, useState, version)
Downgrading or upgrading material and emotion, styled did not work.
In my rollup.config.js I had:
{
input: "src/index.ts",
output: [
{
file: pkg.main,
format: "cjs",
sourcemap: true,
},
{
file: pkg.module,
format: "esm",
sourcemap: true,
},
],
plugins: [ //plugins here ],
},
{
input: "dist/esm/types/index.d.ts",
output: [{file: "dist/index.d.ts", format: "esm"}],
plugins: [dts()],
external: [/\.css$/],
},
esm - esm output is used for building for <script type=module> tag in modern browsers.
cjs – CommonJS, suitable for Node and other bundlers rollup output formats
Fix
New rollup.config.ts that uses only cjs:
{
...
output: [
{
file: pkg.main,
format: "cjs",
sourcemap: true,
},
// REMOVED esm from here
],
plugins: [ //plugins here ],
},
{
input: "dist/types/index.d.ts", // CHANGED <--dist/esm/types/library.d.ts
output: [{file: "dist/index.d.ts", format: "esm"}],
plugins: [dts()],
external: [/\.css$/],
},
(works with "#emotion/react": "^11.8.2",
"#emotion/styled": "^11.8.1",
"#mui/material": "^5.5.2")
Sample repositories:
component library Git repo
consumer of the library Git repo
(check README.md for more info.)
It's possible that there is an error in the latest version of #emotion/react, 11.8.0. It was released 3h ago which would explain why it stopped working all of a sudden.
As a workaround, install the older version #emotion/react#11.7.1 to fix the problems.
This issue for other components in mui lib too, you can downgrade emotion/react to 11.7.1 till it's fixed 🚀
Same happen for me! Downgrading to 11.7.1 fixed!

NextJS styled-components giving an error in bundle file

I am using styled-components for my React project. After I bundled my react application with rollupjs then I am trying to use this bundle with NextJS. It is getting error in NextJS:
ReferenceError: window is not defined
When I look line of error in the bundle file this error is happening inside of styled-components.
Here is my rollup.config.js in React App (not Nextjs):
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 pkg from "./package.json";
// PostCSS plugins
import simplevars from "postcss-simple-vars";
import nested from "postcss-nested";
import cssnext from "postcss-cssnext";
import cssnano from "cssnano";
import cssvariables from "postcss-css-variables";
const extensions = [".js", ".jsx", ".ts", ".tsx"];
export default {
input: ["./src/index.js"],
output: [
{
file: pkg.main,
format: "cjs",
globals: { react: "React" },
},
{
file: pkg.module,
format: "esm",
},
],
plugins: [
external(["react", "uikit"]),
postcss({
plugins: [
simplevars(),
cssvariables(),
nested(),
cssnext({ warnForDuplicates: false }),
cssnano(),
],
extensions: [".css"],
}),
babel({
exclude: "node_modules/**",
extensions,
}),
// Allows node_modules resolution
resolve({
mainFields: ["module", "main", "jsnext:main", "browser"],
dedupe: ["react", "react-dom"], // Default: []
extensions,
}),
commonjs(),
image(),
visualizer(),
],
};
How can I solve this error in NextJS with bundle code?
Thanks
It means your window related code is executed on the Node.js side, not on the browser. Since Next.js is server-side rendering framework, you should make sure that you don't use browser thing (like window object) in the code that is executed on the server side.
Please reference this article.
UPDATE
Please check your .babelrc -
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}

Tree-shaking with rollup

I have a project in which I bundle a components library using Rollup (generating a bundle.esm.js file). These components are then used in another project, that generates web pages which use these components - each page is using different components.
The problem is, that the entire components library is always bundled with the different page bundles, regardless of which components I'm using, unnecessarily increasing the bundle size.
This is my Rollup setup:
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import pkg from './package.json';
const extensions = [
'.js', '.jsx', '.ts', '.tsx',
];
export default [
{
input: './src/base/index.ts',
plugins: [
peerDepsExternal(),
resolve({ extensions }),
babel({
exclude: 'node_modules/**',
extensions,
}),
commonjs(),
],
output: [
{ file: pkg.main, format: 'cjs', sourcemap: true },
{ file: pkg.module, format: 'es', sourcemap: true },
],
watch: {
clearScreen: false,
},
},
];
I have "modules" set to false in webpack, as well.
There are things you will need to do to achieve treeshakable code from both sides - the built package and the project using it.
From your code snippet, I see that you have not add flag preserveModules: true in the rollup config file to prevent the build output from bundling. Webpack can not treeshake a bundled file FYI.
export default {
...
preserveModules: true,
...
}
On the side of the project that using it, you have to specify sideEffects in the package.json - read the doc to know how to config them. Beside that, the optimization in webpack has to has sideEffects: true, also read the doc here.
Hope this helps!
As you don't know which components of your Component Library (CL) will be needed by the adopters repositories you need to export everything but in a way
the adopters can execute a tree-shaking on your CL when they do their own build (and just include what they really need).
In a few words, you have to make your CL, tree-shakable. In order to achieve this, on your CL repo you have to:
Use bundlers that support tree-shaking (rollup, webpack, etc..)
Create the build for modules of type es/esm, NOT commonJS/cjs, etc..
Ensure no transpilers/compilers (babel,tsconfig, etc..) usually used as plugins, transform your ES module syntax to another module syntax.
By the default, the behavior of the popular Babel preset #babel/preset-env may break this rule, see the documentation for more details.
// babelrc.json example that worked for me
[
"#babel/preset-env",
{
"targets": ">0.2%, not dead, not op_mini all"
}
],
In the codebase, you always have to use import/export (no require) syntax, and import specifically the things you need only.
import arrayUtils from "array-utils"; //WRONG
import { unique, implode, explode } from "array-utils"; //OK
Configure your sideEffects on the package.json.
"sideEffects": ["**/*.css"], //example 1
"sideEffects": false, //example 2
DO NOT create a single-bundle file but keep the files separated after your build process (official docs don't say this but was the only solution that worked for me)
// rollup.config.js example
const config = [
{
input: 'src/index.ts',
output: [
{
format: 'esm', // set ES modules
dir: 'lib', // indicate not create a single-file
preserveModules: true, // indicate not create a single-file
preserveModulesRoot: 'src', // optional but useful to create a more plain folder structure
sourcemap: true, //optional
},
],
... }]
Additionally, you may need to change your module entry point in order the adopters can directly access to the proper index.js file where you are exporting everthing:
// package.json example
{
...
"module": "lib/index.js", //set the entrypoint file
}
Note: Remember that tree-shaking is executed by an adopter repository that has a build process that supports tree-shaking (eg: a CRA repo) and usually tree-shaking is just executed on prod mode (npm run build), no on dev mode. So be sure to properly test if this is working or not.

Unable to load a react module as node module

I have a react component in the path
src/components/test
import React from 'react';
import ReactDom from 'react-dom';
class TestComp extends React.Component {}
export default TestComp;
I am exposing the component in index.js from path
src/index.js
import TestComp from './components/test';
export {
TestComp
};
I have added main in package.json as "main": "src/index.js"
I have published a npm package test-comp of above application and using same in another application.
main.js
import {TestComp} from 'test-comp';
I am using grunt-browserify in this application with following options set.
options: {
"transform": [
[
"babelify",
{
"presets": [
"es2015",
"react",
"stage-0"
]
}
]
],
browserifyOptions: {
debug: true,
extensions: ['.js', '.jsx'],
entries: ['main.js']
}
}
When I run grunt browserify getting following error.
>> import TestComp from './components/test';
>> ^
>> ParseError: 'import' and 'export' may appear only with 'sourceType: module'
Warning: Error running grunt-browserify. Use --force to continue.
It probably not understanding the path mentioned in node module or rejecting to understand the same which linting. I even have tried adding following in .eslintrc but no luck
{
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"env": {
"browser": true,
"es6": true
},
"ecmaFeatures": {
"modules": true
}
}
I tried most of SO answers related to this error. But still stuck in same place.
EDIT
I am able to browserify first module directly with almost similar configuration. Getting this error when first module is loaded as node dependancy in other application as explained above.
So you wrote the module test-comp in ES6, using import and export, and the main entry of the package.json in test-comp refers to src/index.js.
The answer is that browserify transforms don't apply to every module you require. They only apply to the immediate project: not the project's dependencies.
If you want to require a module that uses ES6 syntax in browserify, you'll either need to
Add a prepublish script to test-comp that transpiles it to ES5, and change the main entry of test-comp to refer to that ES5 version, not the ES6 version
Add babelify as a dependency of test-comp and add babelify as a browserify transform in the package's 'browserify' entry, as documented in babelify.

Rollup.js: undefined objects in external dependencies

I recently started playing with rollupjs. After configuring everything as per available docs and bundling things up, I got many errors from my external libraries about undefined objects. This sort of errors: Cannot read property 'parse' of undefined coming from crypto-js.
It complains about this line in the code: var ciphertext = Base64.parse(openSSLStr). So Base64 is undefined. I have few errors like this from different external libraries bundled in.
I use a handful of external dependencies:
chart.js,
crypto-js,
mithril,
moment,
pluralize
All of them work perfectly with jspm. I decided to try rollup to speed things up as jspm is soooo slow at the moment. Now half of my external dependencies stopped working. I get "undefined things" and "...not a function" kind of errors coming from external libraries only.
What could possibly be the cause of it?
This is my rollup.config.js
import babel from 'rollup-plugin-babel';
import npm from 'rollup-plugin-npm';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/scripts/application/main.js',
format: 'cjs',
plugins: [
npm({
jsnext: true,
main: true,
}),
babel({
exclude: 'node_modules/**',
presets: [ 'es2015-rollup' ],
}),
commonjs(),
uglify(),
],
dest: 'static/js/application.js',
};
Let me know if any other details are needed.
Thanks.
EDIT
I've done a simple tests-reproduction bundling those libraries that generate errors in my application.
package.json
{
"name": "minion",
"private": true,
"babel": {
"presets": [
"es2015-rollup"
]
},
"dependencies": {
"chart.js": "^1.0.2",
"crypto-js": "^3.1.6",
"mithril": "^0.2.2-rc.1",
"moment": "^2.11.1",
"pluralize": "^1.2.1"
},
"devDependencies": {
"babel-preset-es2015-rollup": "^1.1.1",
"rollup-plugin-babel": "^2.3.9",
"rollup-plugin-commonjs": "^2.2.0",
"rollup-plugin-npm": "^1.3.0",
"rollup-plugin-uglify": "^0.1.0"
}
}
rollup.config.js
import babel from 'rollup-plugin-babel';
import npm from 'rollup-plugin-npm';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/main.js',
format: 'cjs',
plugins: [
npm({
jsnext: true,
main: true,
}),
babel({
exclude: 'node_modules/**',
presets: [ 'es2015-rollup' ],
}),
commonjs(),
//uglify(),
],
dest: 'static/js/app.js',
}
main.js
import Application from './application'
import pluralize from 'pluralize'
var text = Application.run()
console.log(`Testing encryption: ${text}`)
console.log(`Testing pluralization: ${pluralize('person')}`)
application.js
import crypt from 'crypto-js'
var Application = {
run() {
var ciphertext = crypt.AES.encrypt('Testing encryption...', 'password')
var bytes = crypt.AES.decrypt(ciphertext.toString(), 'password')
return bytes.toString(crypt.enc.Utf8)
}
}
export default Application
Running the above will generate the errors.
Just speculating: Maybe is a bug of the rollup and/or crypto.
I have a similar error when trying to run a js function in Node Red, the js is ok when I run it locally but it throws TypeError: Cannot read property 'split' of undefined when runs remotely.
The only thing that my code have in common with yours is that both uses cryptography, specifically crypto-js 3.1.2 rollup "hmac-sha256.js" and the code is not imported but raw.
Even after deleting the only instance of 'split' yet I can't solve it (but keeps running locally)

Categories