Configuring rollup for a react library - reactjs

I am sorry, I didn't where else to post this.
This is my rollup configuration
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 pkg from './package.json';
export default {
input: 'src/index.js',
external: ['react', 'react-dom', 'prop-types'],
output: [
{ file: pkg.main, format: 'cjs', exports: 'named' },
{ file: pkg.module, format: 'es', exports: 'named' },
],
plugins: [
resolve(),
commonjs({
include: 'node_modules/**',
}),
babel({
exclude: 'node_modules/**',
}),
terser(),
],
};
Here is the entire source code of my project https://github.com/withvoid/melting-pot
It is published on npmjs
https://www.npmjs.com/package/#withvoid/melting-pot
my problem is, my library works great if I add it in a https://github.com/facebook/create-react-app project, but when I add it in a codesandbox project
https://codesandbox.io/s/6lqzp7q28w
It gives me an error that
Invariant Violation
Hooks can only be called inside the body of a function component.
I can't seem to figure out if this is a codesandbox issue (which I am doubtful of) or an issue with my rollup configuration.

The issue is that melting-pot has "react" and "react-dom" specified as dependencies, but they should be specified as peerDependencies. This is having the effect of pulling React in twice with ill effects. If I remove the react dependencies from the sandbox entirely, it works because then React is only being pulled in once (by melting-pot). Obviously this isn't the appropriate resolution, but it is a quick way to verify the cause of the error.

Related

Rollup build a React project import the modules in Node.js environment and get "TypeError: Cannot set properties of undefined (setting 'Headers')"

I have a Github package built with React by Rollup.
In a project where it imports the modules from the Github package, I'm trying to run tests by Jest.
In every line of module import in the test files, I get the following error.
TypeError: Cannot set properties of undefined (setting 'Headers')
I'm assuming the rollup build config is the problem.
import typescript from '#rollup/plugin-typescript';
import commonjs from '#rollup/plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import resolve from '#rollup/plugin-node-resolve';
import json from '#rollup/plugin-json';
import packageJson from './package.json';
export default {
input: 'src/index.tsx',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
exports: 'named',
},
{
file: packageJson.module,
format: 'cjs',
sourcemap: true,
exports: 'named',
},
],
plugins: [
external(),
resolve({
browser: true,
}),
typescript({
exclude: ['**/**.stories.tsx', '**/*.test.ts'],
}),
commonjs({
include: ['node_modules/**'],
}),
json(),
],
};
The above is my rollup build settings.
Does anyone know what the problem is?

Preserving SCSS variables during rollup build

I am working on a simple react component library which has atomic components styled using scss. Each component has its own stylesheet which imports a global stylesheet. The global stylesheet has variables defined for colours, gradients, borders etc.
When I do rollup build, the output of this has processed styles attached to each component, i.e. the variables are replaced with actual values and css string is formed.
This library will be used by another project which will decide the values of these variables. I want to give some default values in my component library but allow the project to override these values.
rollup.config.js
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "#rollup/plugin-node-resolve";
import commonjs from "#rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import postcss from "rollup-plugin-postcss";
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,
}
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript({ useTsconfigDeclarationDir: true }),
postcss({
extensions: ['.scss']
})
]
};
Some styles in my common.scss
$primary-text: #3C3C3C !default;
$secondary-text: #5C5C5C !default;
$tertiary-text: #7C7C7C !default;
How can I configure it so that project will be able to override the styles by redeclaring values of these variables? I expect that there will be one global.scss in project which will import my common.scss from library and then override required variables. Is rollup good option for this kind of use case? Please let me know if any other snippet is required from my current configuration.
Thank you.
postcss({
extract: true,
minimize: true,
use: [
[
'sass',
{
data: '#import "./common.scss"; '
}
]
],
plugins: []
})
https://github.com/hytromo/rollup-plugin-postcss-retain-sass-data#readme

css module support using react Rolllup

I'm working on building out a react package (not app) and have run into my introduction to using rollup which, as I understand it, the equivalent to using webpack for react app development.
As I'm moving my code over I've run into a snag where I can't seem to work out the configuration to allow for css/scss module support, e.g.,
import React from "react";
import styles from "./Button.css"; <--- TS2307: Cannot find module './Button.css' or its corresponding type declarations.
my configuration looks like this:
import resolve from "#rollup/plugin-node-resolve";
import commonjs from "#rollup/plugin-commonjs";
import typescript from "#rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import dts from "rollup-plugin-dts";
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
}
],
plugins: [
resolve(),
postcss({
extract: false,
modules: true,
use: ['sass'],
}),
commonjs(),
typescript({ tsconfig: "./tsconfig.json" })
]
},
{
input: "dist/esm/types/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "esm" }],
plugins: [dts()],
external: [/\.(css|less|scss)$/]
}
];
but I can't get passed the error:
TS2307: Cannot find module './Button.css' or its corresponding type declarations.
my web research has found some attempts at solving this, but nothing has worked. I can drop the css directly on the component:
import React from "react";
import "./Button.css";
But the assignment doesn't work. Any ideas on solution?

Rollup ESM generates broken imports

I want to bundle a typescript react App as a component into a ES module or UMD.
But the generated ES bundle produces an invalid module js.
On bundle it gives me this hints. But I cant find any solution for this.
(!) Missing global variable names
Use output.globals to specify browser global variable names corresponding to external modules
http (guessing 'http')
...
inside the esm js bundle there are imports like these:
import http from 'http';
import https from 'https';
import url from 'url';
import require$$0 from 'stream';
...
function createCommonjsModule(fn) {
var module = { exports: {} };
return fn(module, module.exports), module.exports;
}
And after adding it to the browser:
<script type="module" src="./index.esm.js"></script>
I got the error about the missing relative imports:
Uncaught TypeError: Failed to resolve module specifier "http". Relative references must start with either "/", "./", or "../".
Iam surely have mistakes on my rollup configuration, but I cant find the spot and happy and thankful about any hints.
...
Of course I have nodemodule imports in my app like:
import {observer} from 'mobx-react';
But rollup should handle this. Dont he?
Here is my rollup.config:
import pkg from './package.json';
import nodeResolve from "#rollup/plugin-node-resolve";
import typescript from "rollup-plugin-typescript2";
import image from "#rollup/plugin-image";
import styles from "rollup-plugin-styles";
import commonjs from "#rollup/plugin-commonjs";
import replace from "#rollup/plugin-replace";
import json from '#rollup/plugin-json';
import babel from '#rollup/plugin-babel';
import copy from "rollup-plugin-copy";
import del from "rollup-plugin-delete";
export default {
input: pkg.source,
output: [
{
file: pkg.module,
format: 'es',
sourcemap: false
},
{
file: "dist/index.umd.js",
format: 'umd',
sourcemap: true
},
],
plugins: [
del({targets: 'dist/*'}),
nodeResolve({
mainFields: ['jsnext:main', 'module', 'main'],
dedupe: [ 'react', 'react-dom' ]
}),
replace({
preventAssignment: false,
'process.env.NODE_ENV': JSON.stringify('development'),
__buildDate__: () => JSON.stringify(new Date())
}),
json(),
typescript(),
styles(),
copy({
targets: [
{src: 'public/**/*', dest: 'dist'}
]
}),
babel({ //disabled cause WebComponent integration
presets: ["#babel/preset-react"],
exclude: 'node_modules/**',
babelHelpers: 'bundled'
}),
commonjs(),
image()
]
};
I had a similar issue (though with stream module, not http), and for me the solution was to set browser: true in nodeResolve within rollup.config:
export default [
{
input: '...',
plugins: [
nodeResolve({
'browser': true,
}),
commonjs(),
],
output: {
...
}
}
];
From description of a flag:
browser
Type: Boolean
Default: false
If true, instructs the plugin to use the browser module resolutions in package.json and adds 'browser' to exportConditions if it is not present so browser conditionals in exports are applied. If false, any browser properties in package files will be ignored. Alternatively, a value of 'browser' can be added to both the mainFields and exportConditions options, however this option takes precedence over mainFields.

Rollup React Library Output Multiple Build Folders?

I have created a React Library with rollup, however, I have a large number of components that get exported so the file size is relatively large.
So in a project where I import the library doing the following;
import { ComponentExampleOne, ComponentExampleTwo } from 'my-react-library';
It imports the whole index file outputted via rollup (including all other components and any 3rd party dependencies), so when a user first hits the page with the import above they need to download the whole file, which is a lot bigger than I would like it to be.
For the likes of lodash where I just want to access a single function and not the entire library, I would do the following;
import isEmpty from 'lodash/isEmpty';
I want to achieve similar functionality with rollup so I can do something like
import { ComponentExampleOne } from 'my-react-library/examples';
import { ButtonRed } from 'my-react-library/buttons';
So I only import what is exported in the index.js file within an examples and buttons folder with this is as my folder structure in my library.
my-react-library/
-src/
--index.js
--examples/
---ComponentExampleOne.js
---ComponentExampleTwo.js
---ComponentExampleThree.js
---index.js
--buttons/
---ButtonRed.js
---ButtonGreen.js
---ButtonBlue.js
---index.js
I have no idea to achieve this with rollup?
This is my current rollup.config.js
import babel from 'rollup-plugin-babel';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import filesize from 'rollup-plugin-filesize';
import localResolve from 'rollup-plugin-local-resolve';
import json from 'rollup-plugin-json';
import pkg from './package.json';
import externals from 'rollup-plugin-node-externals';
import builtins from 'rollup-plugin-node-builtins';
import globals from 'rollup-plugin-node-globals';
import image from 'rollup-plugin-inline-image';
import { terser } from 'rollup-plugin-terser';
const config = {
input: 'src/index.js',
watch: {
chokidar: {
usePolling: true,
paths: 'src/**'
}
},
output: [
{
file: pkg.browser,
format: 'umd',
name: 'Example'
},
{
file: pkg.main,
format: 'cjs',
name: 'Example'
},
{
file: pkg.module,
format: 'es'
},
],
external: Object.keys(pkg.peerDependencies || {}),
plugins: [
globals(),
builtins(),
externals(),
babel({ exclude: 'node_modules/**', presets: ['#babel/env', '#babel/preset-react'] }),
commonjs({
include: "node_modules/**",
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/formik/node_modules/scheduler/index.js': ['unstable_runWithPriority'],
}
}),
peerDepsExternal(),
postcss({ extract: true }),
json({ include: 'node_modules/**' }),
localResolve(),
resolve({
browser: true,
dedupe: ['react', 'react-dom'],
}),
filesize(),
image(),
terser()
]
};
export default config;
Any help would be greatly appreciated.
You don't really need to do that if you use named exports and any modern bundler for building the app.
When Rollup detects you are not using some export it will be removed due to tree-shaking.
If you still want to do it pass an object with the different entries you want to the input option:
// ...
const config = {
input: {
examples: 'examples/entry/file.js',
buttons: 'buttons/entry/file.js'
},
// ...
}

Resources