CommonJS rollup plugin syntax error when importing 3rd party libraries, mostly related to 'process' - reactjs

I've been working on a custom rollup config which takes a React project, and inlines the js and css in index.html.
When I've imported some third party react libraries (like material-ui-color) I run into a problem with CommonJS where it says there's a syntax error. Of the libraries I've tried to install so far, the word 'process' is most commonly the word it breaks on. Here is the log from the material-ui-color issue:
[!] (plugin commonjs) SyntaxError: Unexpected token (205:28) in /Users/meyerm/Documents/GitHub/button-generator-figma-plugin/node_modules/jss/dist/jss.esm.js
node_modules/jss/dist/jss.esm.js (205:28)
203: var newValue = value;
204:
205: if (!options || options.process !== false) {
^
I've included the rollup-plugin-replace to fix any occurrences of process.env.NODE_ENV but this seems to be a different issue.
Here is my rollup config:
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import babel from '#rollup/plugin-babel';
import livereload from 'rollup-plugin-livereload';
import replace from '#rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import html from 'rollup-plugin-bundle-html-plus';
import typescript from 'rollup-plugin-typescript';
import svgr from '#svgr/rollup';
const production = !process.env.ROLLUP_WATCH;
export default [
/*
Transpiling React code and injecting into index.html for Figma
*/
{
input: 'src/app/index.tsx',
output: {
name: 'ui',
file: 'dist/bundle.js',
format: 'umd',
},
plugins: [
// What extensions is rollup looking for
resolve({
extensions: ['.jsx', '.js', '.json', '.ts', '.tsx'],
}),
// Manage process.env
replace({
preventAssignment: true,
process: JSON.stringify({
env: {
isProd: production,
},
}),
'process.env.NODE_ENV': JSON.stringify(production),
}),
typescript({ sourceMap: !production }),
// Babel config to support React
babel({
presets: ['#babel/preset-react', '#babel/preset-env'],
babelHelpers: 'runtime',
plugins: ['#babel/plugin-transform-runtime'],
extensions: ['.js', '.ts', 'tsx', 'jsx'],
compact: true,
exclude: 'node_modules/**',
}),
commonjs({
include: 'node_modules/**',
}),
svgr(),
// Config to allow sass and css modules
postcss({
extensions: ['.css, .scss, .sass'],
modules: true,
use: ['sass'],
}),
// Injecting UI code into ui.html
html({
template: 'src/app/index.html',
dest: 'dist',
filename: 'index.html',
inline: true,
inject: 'body',
ignore: /code.js/,
}),
// If dev mode, serve and livereload
!production && serve(),
!production && livereload('dist'),
// If prod mode, minify
production && terser(),
],
watch: {
clearScreen: true,
},
},
/*
Main Figma plugin code
*/
{
input: 'src/plugin/controller.ts',
output: {
file: 'dist/code.js',
format: 'iife',
name: 'code',
},
plugins: [resolve(), typescript(), commonjs({ transformMixedEsModules: true }), production && terser()],
},
];
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
// Start localhost dev server on port 5000 to work on the UI in the browser
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true,
});
}
},
};
}
Any ideas how I can overcome this and import 3rd party libraries into the project easily?

I recommend rewriting the config in the following way:
replace({
"process.env.isProd": production,
}),

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()],
},
];

React module federation micro frontend- webpack 5 public path auto is not working

In my react micro frontend project which uses module federation plugin remoteEntry.js path is incorrectly fetched and application crashes.
With hard-coded publicPath: "http://localhost:3000/" in webpack things work as expected.
But due to existing CI/CD setup in my project, publicPath in the webpack config cannot be hard-coded at the build time.
As per various articles it was recommended to use publicPath : auto in the webpack config of the application.
Yes publicPath : auto solves the issue of hard coding at build time.
But during page refresh, remoteEntry.js is fetched from incorrect url.
To simulate the scenario I created simple react application using webpack and module federation plugin
On the initial load remoteEntry.js is fetched from http://localhost:3000/remoteEntry.js as expected
When under nested URL http://localhost:3000/home/foo/about, On refresh main.js and remoteEntry.js is fetched from http://localhost:3000/home/foo/remoteEntry.js - application crashes
Github link for the project simulating the scenario
https://github.com/jidu0106/mf-page-refresh-issue
webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
output: {
publicPath: "auto",
},
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
},
devServer: {
port: 3000,
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.m?js/,
type: "javascript/auto",
resolve: {
fullySpecified: false,
},
},
{
test: /\.(css|s[ac]ss)$/i,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "mf_page_refresh_issue",
filename: "remoteEntry.js",
remotes: {},
exposes: {
'./Component' : './src/test-component.js'
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
},
}),
new HtmlWebPackPlugin({
template: "./src/index.html",
}),
],
};
I am using "react-router-dom": "^6.3.0" and also tried with "#reach/router": "^1.3.4", but the issue is same
Tried solutions given in https://github.com/module-federation/module-federation-examples/issues/102 but it didn't work
Last suggestion from ScriptedAlchemy in the above link is to use publicPath : auto in 2022
Any help would much appreciated. Thanks in advance.
In case anyone is facing the same issue, add another publicPath : '/' in HtmlWebpackPlugin().
something like the following
new HtmlWebPackPlugin({
template: './public/index.html',
favicon: './public/favicon.png',
assets: './public/assets',
publicPath: '/',
}),
Referred workaround from the following link and it helped
https://github.com/module-federation/module-federation-examples/pull/2170

Webpack Module Federation fails with Next JS when using styled-components

I'm trying to get Webpack Module Federation working with a NextJS app where the federated module is using styled-components. My configuration is working as expected if I'm only using react and react-dom but using styled-components gives me these errors:
It looks like there are several instances of 'styled-components' initialized in this application. This may cause dynamic styles to not render properly, errors during the rehydration process, a missing theme prop, and makes your application bigger without good reason.
and
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
I have tried the different approaches to resolve styled-components to a single instance by modifying the Webpack config in both the host and remote apps, but all seem to fail.
Here is my current configuration:
Federated modules repo (webpack.config.js):
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
const path = require("path");
module.exports = {
mode: "development",
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 3001,
},
output: {
publicPath: "auto",
},
resolve: {
extensions: [".js"],
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/,
options: {
plugins: [
["babel-plugin-styled-components", { pure: true, ssr: true }],
],
presets: ["#babel/preset-react"],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "remoteLib",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/Button",
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
"styled-components": {
singleton: true,
requiredVersion: deps["styled-components"],
},
},
}),
],
};
Consuming NextJS app Webpack config (next.config.js):
webpack: (config, options) => {
const { ModuleFederationPlugin } = options.webpack.container;
config.plugins.push(
new ModuleFederationPlugin({
remotes: {
'federated-components': 'remoteLib#http://localhost:3001/remoteEntry.js'
}
})
);
return config;
}

rollup MISSING_NODE_BUILTINS error when I include it

I keep getting this error:
src/legacy/widgetlib.tsx → dist/withReact16/browser.js...
{
code: 'MISSING_NODE_BUILTINS',
message: "Creating a browser bundle that depends on Node.js built-in module ('punycode'). You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins",
modules: [ 'punycode' ],
toString: [Function]
}
I have included rollup-plugin-node-builtins in multiple different ways and I've googled all over the place. Every "solution" I've found seems to basically be the same thing, but it isn't working for me. I am also not even directly using punycode. Two of my dependencies have it as a dependency. I am using twitter-text lib and oauth-signature. If I comment out those two imports I no longer get this problem. Here is my complete rollup.config.js file:
import resolve from '#rollup/plugin-node-resolve';
import postcss from 'rollup-plugin-postcss';
import commonjs from '#rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import json from '#rollup/plugin-json';
import image from '#rollup/plugin-image';
import replace from 'rollup-plugin-replace';
import gzipPlugin from 'rollup-plugin-gzip';
import { terser } from 'rollup-plugin-terser';
import includePaths from 'rollup-plugin-includepaths';
import builtins from 'rollup-plugin-node-builtins';
import globals from 'rollup-plugin-node-globals';
import React from 'react';
import ReactDOM from 'react-dom';
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
const { PRODUCTION } = process.env;
const CODES = [
'THIS_IS_UNDEFINED',
'MISSING_GLOBAL_NAME',
'CIRCULAR_DEPENDENCY',
];
const globalVars = {
react: 'React',
'react-dom': 'ReactDOM',
};
const discardWarning = warning => {
if (CODES.includes(warning.code)) {
return;
}
// eslint-disable-next-line no-console
console.error(warning);
};
const commonConfig = {
onwarn: discardWarning,
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify(
PRODUCTION ? 'production' : 'development'
),
}),
image(),
globals(),
builtins(),
resolve({
jsnext: true,
extensions,
preferBuiltins: true,
browser: true,
mainFields: ['browser', 'jsnext', 'module', 'main'],
}),
includePaths({
paths: ['src'],
extensions: [...extensions, '.scss', '.json'],
}),
commonjs({
include: 'node_modules/**',
namedExports: {
react: Object.keys(React),
'react-dom': Object.keys(ReactDOM),
},
}),
babel({
extensions,
runtimeHelpers: true,
babelrc: true,
exclude: 'node_modules/**',
}),
json(),
postcss({
plugins: [],
}),
terser(),
],
};
const browserLibWithReact16 = {
...commonConfig,
input: 'src/legacy/widgetlib.tsx',
output: {
format: 'iife',
sourcemap: true,
name: 'WLIB',
file: 'dist/withReact16/browser.js',
},
plugins: [...commonConfig.plugins, gzipPlugin()],
};
const npmWLIBWithReact16 = {
...commonConfig,
input: 'src/widgetlib.tsx',
output: {
file: 'dist/withReact16/WLIB.js',
format: 'esm',
sourcemap: true,
},
};
const npmLibNoReact = {
...commonConfig,
external: Object.keys(globalVars),
input: 'src/widgetlib.tsx',
output: {
file: 'dist/index.js',
format: 'esm',
sourcemap: true,
},
};
export default [npmLibNoReact, npmWLIBWithReact16, browserLibWithReact16];
The error is only happening for the browserLibWithReact16 config.
Any help would be appreciated.

How to prepare react component as external package using webpack?

I have react component hosted on github, it is simple wrapper around <table> with few features. I use webpack to build my project. This is webpack config inherited after react-create-app:
module.exports = {
bail: true,
devtool: 'source-map',
entry: {
index: paths.appIndexJs
},
externals : {
react: 'react'
},
output: {
path: paths.appBuild,
filename: 'index.js',
publicPath: publicPath,
library: "ReactSimpleTable",
libraryTarget: "umd"
},
resolve: {
fallback: paths.nodePaths,
extensions: ['.js', '.json', '.jsx', '']
},
module: {
preLoaders: [
... preloaders ...
],
loaders: [
... loaders ...
]
},
plugins: [
new webpack.DefinePlugin(env),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new ExtractTextPlugin('static/css/[name].[contenthash:8].css'),
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
And now when I used my component in another project (as external package from github) I've got warnings related to that issue: https://facebook.github.io/react/warnings/dont-call-proptypes.html .
If I remove externals from webpack config everything works fine, but my output file is about 130kB (sic!). With externals in webpack config react is excluded from index.js and it weights about 35kB (non minified). But I got warnings :/
I wonder how to exclude react from build and mitigate warnings. I don't use PropTypes in any unusual way, so advices from https://facebook.github.io/react/warnings/dont-call-proptypes.html are not relevant.
I just want to let users import my component and assume, that they have react already in dependencies...

Resources