How to avoid code duplication between entry points in Rollup? - javascript

I want to configure rollup to take a bunch of files on input & produce a bunch of files in dist that share some common code between them.
Here's the rollup config I use:
import path from 'path';
import pathsTransformer from 'ts-transform-paths';
import alias from '#rollup/plugin-alias';
import commonjs from '#rollup/plugin-commonjs';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import typescript from 'rollup-plugin-typescript2';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
const plugins = [
peerDepsExternal(),
alias({
entries: [
{ find: '#', replacement: path.join(__dirname, '/src') },
{ find: '$root', replacement: __dirname },
],
}),
nodeResolve(),
typescript({
transformers: [() => pathsTransformer()],
}),
commonjs({
extensions: ['.js', '.ts'],
}),
];
export default [
{
input: './src/a.ts',
output: {
file: 'dist/a.js',
format: 'esm',
sourcemap: true,
},
plugins,
},
{
input: './src/b.ts',
output: {
file: 'dist/b.js',
format: 'esm',
sourcemap: true,
},
plugins,
},
];
The problem with this set-up is that if a.ts & b.ts both depend on some code, this common code gets bundled into each output file unnecessarily increasing the bundle size.
Since the output.format is esm (so, imports are available in the output), I'd rather expect rollup to split the shared code between the 2 files in a separate chunk & then make both files import that common code (which seems to be the thing rollup does by default anyway).
I assume the problem to be somewhere around nodeResolve or commonjs calls, but, I want my dependencies to be bundled. I just don't want to have them duplicated.
How do I optimize my output? Here's a reproduction to visualize it (dist included).

By returning an array of objects, you specify to rollup that you want to have independent bundles. If you just want to produce multiple files (which was exactly my case) you can just specify input as an object.
import path from 'path';
import pathsTransformer from 'ts-transform-paths';
import alias from '#rollup/plugin-alias';
import commonjs from '#rollup/plugin-commonjs';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import typescript from 'rollup-plugin-typescript2';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
const plugins = [
peerDepsExternal(),
alias({
entries: [
{ find: '#', replacement: path.join(__dirname, '/src') },
{ find: '$root', replacement: __dirname },
],
}),
nodeResolve(),
typescript({
transformers: [() => pathsTransformer()],
}),
commonjs({
extensions: ['.js', '.ts'],
}),
];
export default {
input: {
a: './src/a.ts',
b: './src/b.ts',
},
output: {
dir: 'dist',
format: 'esm',
sourcemap: true,
},
plugins,
};

Related

Chart v4 : Module not found: Error: Package path . is not exported from package node_modules/chart.js

I'm maintaining a react component library that uses chart.js as a dependency (Not peerDependency).
I upgraded chart.js from 3.9.1 to 4.0.1.
My library still compiles fine but now it throws an error on my react app when I'm importing the lib :
Module not found: Error: Package path . is not exported from package node_modules/chart.js
I'm building everything with rollup here is my config :
import path from 'path';
import babel from '#rollup/plugin-babel';
import commonjs from '#rollup/plugin-commonjs';
import resolve from '#rollup/plugin-node-resolve';
import terser from '#rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import typescript from '#rollup/plugin-typescript';
import image from '#rollup/plugin-image';
import json from '#rollup/plugin-json';
import copy from 'rollup-plugin-copy';
import autoprefixer from 'autoprefixer';
import postcssUrl from 'postcss-url';
import pkg from './package.json' assert { type: 'json' };
import {fileURLToPath} from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { dependencies = {}, peerDependencies = {} } = pkg;
const externals = [...Object.keys(dependencies), ...Object.keys(peerDependencies)];
const src = path.resolve(__dirname, 'src');
const input = path.resolve(src, 'index.ts');
const assets = path.resolve(src, 'assets');
const dest = path.resolve(__dirname, 'dist');
export default [
{
input,
external: (id) => externals.some((dep) => id === dep || id.startsWith(`${dep}/`)),
plugins: [
typescript({ tsconfig: './tsconfig.json' }),
commonjs(),
json(),
resolve({ browser: true }),
babel({
extensions: ['.ts', '.js', '.tsx', '.jsx'],
}),
image(),
postcss({
plugins: [
autoprefixer,
postcssUrl({
url: 'inline',
basePath: assets,
}),
],
}),
copy({
targets: [
{ src: 'src/**/_*.scss.d.ts', dest },
{ src: 'src/**/_*.scss', dest },
],
}),
],
output: [
{
file: pkg.main,
format: 'cjs',
},
{ name: pkg.name, file: pkg.module, format: 'es' },
{
name: pkg.name,
file: pkg.browser,
format: 'umd',
globals: {
react: 'React',
},
},
{
name: pkg.name,
file: pkg['browser:min'],
format: 'umd',
globals: {
react: 'React',
},
plugins: [terser()],
},
],
},
];
Does someone has any idea why it is doing so ?
I tried to delete node_modules and package-lock.json and reinstall both on my library and front app but I still have the same error.
I tried to import from 'chart.js/auto' as mentioned in the documentation but it throws :
Module not found: Error: Package path ./auto is not exported from package node_modules/chart.js
I looked at node_modules/chart.js/package.json file and there is a well defined set of exports there. But as it came with the upgrade and no other library has ever throw me this error I guess it comes from the upgrade.
Found the bug. As I thought it is a problem with the library itself. It cannot be required since the conditional subpath is missing from the package.json file. Nothing I can do. I'll downgrade untill the push a fix.

Rollup library importing file with named and default export not working

I have a rollup library that is trying to use Antd react components. Without external component libraries, everything works fine. If I add the component library (material ui complains of a similar issue), it complains about trying to import variables that are not exported while importing the component library.
rollup.config.js:
export default [
{
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
plugins: [visualizer({ open: useVisualizer })],
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
],
external: ['react', 'react-dom'],
plugins: [
resolve({
preferBuiltins: true
}),
commonjs({
transformMixedEsModules: true,
requireReturnsDefault: 'auto',
esmExternals: true,
// dynamicRequireTargets: ['node_modules/antd/es/**/*.js']
}),
typescript({ tsconfig: './tsconfig.json' }),
folder(),
postcss({
plugins: [],
}),
eslint(),
terser(),
],
},
{
input: './src/index.ts',
output: [{ file: 'dist/index.d.ts', format: 'esm' }],
plugins: [dts()],
external: [/\.css$/],
},
];
Error message:
[!] Error: 'Group' is not exported by node_modules/antd/es/radio/radio.js, imported by node_modules/antd/es/calendar/Header.js
However, when we look at the exports from radio.js, 'Group' is clearly exported from radio/index.js from within the radio directory:
import Group from './group';
import InternalRadio from './radio';
import Button from './radioButton';
export { Button, Group };
var Radio = InternalRadio;
Radio.Button = Button;
Radio.Group = Group;
Radio.__ANT_RADIO = true;
export default Radio;
Header.js imports this like so: import { Button, Group } from '../radio';, so the import SHOULD be going to radio/index.js, however, it appears it is actually going to radio/radio.js instead. No amount of rollup configuration has made this change so far. Any ideas how to get this to resolve to index.js instead of radio.js?
Turns out the normal node-resolve plugin does not resolve "local" imports (i.e. file system paths instead of package paths). The solution here is to also use rollup-plugin-local-resolve, which does resolve these paths.

.css file not added into distribution in js only library build with rollup

I'm writing JS library using rollup-starter-lib as base of my JS library
"version": "1.29.0"
It has main.js file & main.css file, There is no html file.
main.js file is all about DOM manipulation, main.css file is necessary in implementation steps, to easily integrate this JS library, rather giving an instruction to user.
I feel like it's .css file not creating due to tree shaking because of css not used in this JS library itself.
Current Distribution
-dist
--my-faceapi-js-lib.cjs.js
--my-faceapi-js-lib.esm.js
--my-faceapi-js-lib.umd.js
Expected Distribution
-dist
--my-faceapi-js-lib.cjs.js
--my-faceapi-js-lib.esm.js
--my-faceapi-js-lib.umd.js
--my-faceapi-js-lib.css
copy main.css file into dist folder not expecting since it's not minifying.
rollup.config.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import pkg from './package.json';
//import copy from 'rollup-plugin-copy'
//import postcss from 'rollup-plugin-postcss'
export default [
{
input: 'src/main.js',
output: {
name: 'howLongUntilLunch',
file: pkg.browser,
format: 'umd'
},
plugins: [
resolve(), // so Rollup can find `ms`
commonjs() // so Rollup can convert `ms` to an ES module
]
},{
input: 'src/main.js',
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' }
]
}
];
main.js
import * as faceapi from "face-api.js";
...
...
...
import 'main.css';
...
...
Basically there is not usage of main.css within JS library it self. but it's necessary when integrate this library.
You need for that to use the rollup-plugin-postcss plugin (you already have it but it's commented):
// rollup.config.js
import postcss from 'rollup-plugin-postcss'
import path from 'path'
export default {
plugins: [
postcss({
extract: path.resolve('dist/my-faceapi-js-lib.css'),
plugins: []
})
]
}
Issue has been fixed after update rollup version from 1.29.0 to 2.26.3`
(also update relevant packages to rollup lib - not sure about related to fix)
"#rollup/plugin-commonjs": "^15.0.0",
"#rollup/plugin-node-resolve": "^9.0.0",
"rollup": "^2.26.3",
and after adding ['./main.css'], as an external.
{
input: 'src/main.js',
external: ['./main.css'],
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' }
]
}
rollup.config.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import pkg from './package.json';
import postcss from 'rollup-plugin-postcss'
import path from 'path'
export default [
{
input: 'src/main.js',
output: {
name: 'my-faceapi-js-lib',
file: pkg.browser,
format: 'umd'
},
plugins: [
postcss({
minimize: true,
extensions: ['.css'],
extract: path.resolve('dist/face-auth-rnd.css'),
}),
resolve(), // so Rollup can find `ms`
commonjs() // so Rollup can convert `ms` to an ES module
]
},
{
input: 'src/main.js',
external: ['./main.css'],
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' }
]
}
];
This can be fixed by adding an extra export which uses the CSS file as input.
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import pkg from './package.json';
import postcss from 'rollup-plugin-postcss'
export default [
{
input: 'src/main.js',
output: {
name: 'howLongUntilLunch',
file: pkg.browser,
format: 'umd'
},
plugins: [
resolve(),
commonjs()
]
},{
input: 'src/main.css',
output: [
{ file: "dist/main.css" }
],
plugins: [
postcss({
minimize: true,
}),
],
}
];

Rollup config for React component library not working with SSR

I'm trying to set up a React component library using Rollup, which my React app then installs and uses. The consuming React application is being rendered on the server. I have managed to get this set up working with Webpack, wherein I'm bundling my React component library into one single file and sending it down. Problem is this is a huge file and negates the point of SSR (part of it at least) as it takes a long time to download.
So I decided to use Rollup to split up the components into individual ES modules which are then pulled in and used as required by the app. The problem is that I can't get this to work with SSR at all. This is my first time using Rollup so I may have missed something obvious. Here is my rollup.config.js
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import css from 'rollup-plugin-css-only';
import alias from '#rollup/plugin-alias';
import image from '#rollup/plugin-image';
import json from '#rollup/plugin-json';
import replace from '#rollup/plugin-replace';
import namedDeps from './named-dependencies';
import stylus from './rollup-plugin-stylus-v2';
import postcssUrl from './rollup-plugin-postcss-url';
import autoExternal from 'rollup-plugin-auto-external';
import url from '#rollup/plugin-url';
import fs from 'fs';
const namedDepsObject = namedDeps
.map(depPath => ({
keys: Object.keys(require(depPath)).filter(v => v !== 'default'),
name: depPath,
}))
.reduce((acc, val) => {
acc[val.name] = val.keys;
return acc;
}, {});
let ComponentsList = require('./components-file'); // This has an array of component src and name
const rollConfigObject = fileName => {
const configOpts = {
input: `${fileName.src}`,
output: [
{
file: `dist/ssr/esm/${fileName.name}.js`,
format: 'esm',
}
],
plugins: [
autoExternal(),
url({
include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif', '**/*.woff', '**/*.woff2', '**/*.eot', '**/*.ttf', ],
emitFiles: false
}),
babel({
exclude: 'node_modules/**',
presets: ['#babel/env', '#babel/react'],
plugins: ['#babel/plugin-syntax-dynamic-import'],
runtimeHelpers: true,
}),
resolve(),
commonjs({
sourceMap: false,
namedExports: {
...namedDepsObject,
},
}),
image(),
json(),
stylus(),
postcssUrl(),
css({
output: function(styles, styleNodes){
fs.appendFileSync(__dirname + '/dist/ssr/styles.css', styles);
}
}),
],
};
return { ...configOpts };
};
export default ComponentsList.map(
moduleName => {
return rollConfigObject(moduleName);
}
)
Now this creates the separate component bundles like I want, but the problem is that it seems to be including code within the createCommonjsModule methods from node_modules which is then injecting browser objects like document, which when I attempt to render on the server throws an error.
I am trying to not include node_modules at all, so when I try to run this with only the babel plugin, it throws an Error: Could not resolve entry module and refuses to build. An example is Braintree, it's adding a lot of code with document usage.
I'm not sure how to get it to not inject node_modules code within the component. In the app, I'm using Webpack to run these modules through babel, but objects like document will pass through and then refuse to work on the server.
Any suggestions would be great here, been struggling with this for a number of days now. Thanks!
I got the config to work after I updated my babel setting to
babel({
exclude: [
'../../node_modules/**',
'../node_modules/**',
'node_modules/**',
],
presets: [
[
'#babel/env',
{
modules: false,
targets: {
esmodules: true,
},
},
],
'#babel/react',
],
plugins: [
'#babel/plugin-proposal-object-rest-spread',
'#babel/plugin-transform-runtime',
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
],
runtimeHelpers: true,
}),
commonjs({ sourceMap: false }),

(!) Unused external imports. 'reduce' imported from external module 'lodash' but never used

I am using rollup to build my library and I have a dependency on lodash.
but when I run rollup to bundle my code, I get this warning.
(!) Unused external imports
reduce imported from external module 'lodash' but never used
A sample of my code is as follow:
import { reduce } from "lodash"
export function someutilityfunction(args) {
return reduce(args,() => {
// do somthing
}, {}) // A generic use case of reduce function
}
the bundled library works fine.
I have even tried using
import * as _ from "lodash"
and lodash-es instead of lodash
but no success.
Here is my rollup.config.js
import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import filesize from 'rollup-plugin-filesize'
import typescript from 'rollup-plugin-typescript2'
import commonjs from 'rollup-plugin-commonjs'
import uglify from 'rollup-plugin-uglify'
let production = (process.env.NODE_ENV == "production")
export default {
input: 'src/index.ts',
output: {
file: 'lib/index.js',
format: 'cjs',
name: 'my-library',
sourcemap: true
},
external: [
'rxjs',
'axios',
'lodash'
],
plugins: [
resolve(),
typescript({
tsconfigOverride: {
compilerOptions: {
declaration: true,
moduleResolution: "node",
allowSyntheticDefaultImports: true
}
},
// verbosity: 3,
clean: true,
rollupCommonJSResolveHack: true,
abortOnError: false,
typescript: require('typescript'),
}),
commonjs(),
babel({
exclude: 'node_modules/**'
}),
production && uglify(),
filesize()
],
watch: {
include: 'src/**'
}
};
I have used this rollup config before and it has worked fine, until now.
Am I missing something?
And I know the title of the question can be more generic. feel free to improve the post.
Looks like there is a known issue with the tree-shaking within Rollup and Lodash (also D3 has a similar problem):
https://github.com/rollup/rollup/issues/691

Categories