Rollup ESM generates broken imports - reactjs

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.

Related

Fail to integrate scss in my rollup build

What I try to achieve
I'm trying to create a little library for private use - basically just splitting up some code into lib (product) and app (project) code.
All my source code lives in /src folder which contains React, TypeScript and SCSS code. It would be great, if I can use the SCSS imports directly in React like: import './_button.scss';
I have another SCSS file: src/style/_main.scss it includes some mixins, global styles, resets etc.
Config files
import commonjs from '#rollup/plugin-commonjs';
import json from '#rollup/plugin-json';
import resolve from '#rollup/plugin-node-resolve';
import terser from '#rollup/plugin-terser';
import typescript from '#rollup/plugin-typescript';
import url from '#rollup/plugin-url';
import dts from 'rollup-plugin-dts';
import scss from 'rollup-plugin-scss';
import { format, parse } from 'path';
import pkg from './package.json' assert { type: 'json' };
const getTypesPath = (jsFile) => {
const pathInfo = parse(jsFile);
return format({
...pathInfo,
base: '',
dir: `${pathInfo.dir}/types`,
ext: '.d.ts',
});
};
export default [
{
input: 'src/index.ts',
output: [
{
file: pkg.main,
format: 'cjs',
interop: 'compat',
exports: 'named',
sourcemap: true,
inlineDynamicImports: true,
},
{
file: pkg.module,
format: 'esm',
exports: 'named',
sourcemap: true,
inlineDynamicImports: true,
},
],
plugins: [
resolve({ browser: true }),
commonjs({ extensions: ['.js', '.jsx', '.ts', '.tsx'] }),
typescript({ tsconfig: './tsconfig.build.json' }),
url(),
scss({
failOnError: true
}),
json(),
terser(),
],
external: ['react', 'react-dom'],
},
{
input: getTypesPath(pkg.module ?? pkg.main),
output: [{ file: pkg.types, format: 'esm' }],
plugins: [dts()],
},
];
My tsconfig.build.json looks like this:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./types",
"declaration": true,
"declarationDir": "./types",
"allowSyntheticDefaultImports": true
}
}
Where I struggle
The main issue right now is, that it imports my SCSS file in the definition file e.g. button.d.ts looks like:
import type { FunctionComponent } from 'react';
import type { IButtonProps } from './button.type';
import './_button.scss';
export declare const Button: FunctionComponent<IButtonProps>;
[!] RollupError: Could not resolve "./_button.scss" from "build/esm/types/button/button.d.ts"
Which is indeed a problem, but how can I fix it?
I also get the error:
Error:
#use rules must be written before any other rules.
╷
4 │ #use 'src/style/util/mixin';
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
╵
stdin 4:1 root stylesheet
But it does not really make a lot of sense, since my _button.scss partial has this written on the first line.
My Question
How do I make it work so that I can use SCSS in my library? The best case would be that I don't have to touch my original code. I just want to transpile/bundle it so I can use it somewhere else. Any advice?
After hours of probing, I finally made it work.
The issue was, that it bundled all my sass styles and it did not take into account, that I am using the new #use module syntax. Because everything got concatenated into one big file, #use syntax got placed in the middle of the file resulting in an error. Apparently, only the old #import syntax is properly supported by rollup-plugin-scss - or I was just too incapable of making it work.
So I switched to the https://anidetrix.github.io/rollup-plugin-styles/
library to process the files. All I had to do is to use the styles plugin and I set mode: 'inject' it then injected the styles into my [esm|cjs]/index.js file. Upon importing the lib in the app and starting it, the styles got applied.
Besides, I also had to externalize the imports from the index.d.ts.
The updated config file:
import commonjs from '#rollup/plugin-commonjs';
import json from '#rollup/plugin-json';
import resolve from '#rollup/plugin-node-resolve';
import terser from '#rollup/plugin-terser';
import typescript from '#rollup/plugin-typescript';
import url from '#rollup/plugin-url';
import { format, parse } from 'path';
import dts from 'rollup-plugin-dts';
import external from 'rollup-plugin-peer-deps-external';
import styles from 'rollup-plugin-styles';
import pkg from './package.json' assert { type: 'json' };
const getTypesPath = (jsFile) => {
const pathInfo = parse(jsFile);
return format({
...pathInfo,
base: '',
dir: `${pathInfo.dir}/types`,
ext: '.d.ts',
});
};
export default [
{
input: 'src/index.ts',
output: [
{
file: pkg.main,
format: 'cjs',
interop: 'compat',
exports: 'named',
sourcemap: true,
inlineDynamicImports: true,
},
{
file: pkg.module,
format: 'esm',
exports: 'named',
sourcemap: true,
inlineDynamicImports: true,
},
],
external: ['react', 'react-dom'],
plugins: [
external(),
resolve({
browser: true,
}),
url(),
styles({
mode: 'inject'
}),
json(),
commonjs({
extensions: ['.js', '.jsx', '.ts', '.tsx'],
}),
typescript({
tsconfig: './tsconfig.build.json',
}),
terser(),
],
},
{
input: getTypesPath(pkg.module ?? pkg.main),
output: [
{
file: pkg.types,
format: 'esm',
},
],
external: [/\.(sass|scss|css)$/] /* ignore style files */,
plugins: [dts()],
},
];

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?

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 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'
},
// ...
}

Configuring rollup for a react library

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.

Resources