How to extract all used polyfill from multiple libs by rollup? - javascript

I use rollup to create multiple libs, rollup config file as below:
import terser from '#rollup/plugin-terser';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import { babel } from '#rollup/plugin-babel';
import eslint from '#rollup/plugin-eslint';
const distDir = "dist/libs"
const api = {
input: "src/libs/api/index.js",
external: ["axios"],
output: [
{
file: `${distDir}/api.umd.js`,
format: "umd",
name: 'Api',
globals: {axios: 'axios'}
},
{
file: `${distDir}/api.es.js`,
format: "es"
}
],
plugins: [
terser(),
babel({babelHelpers: 'runtime', exclude: 'node_modules/**'}),
nodeResolve({
browser:true,
}),
commonjs(),
eslint({
throwOnError: true,
throwOnWarning: true,
include: ['src/**'],
exclude: ['node_modules/**']
})
]
}
const test = {
...
}
export default ()=>{
return [
api, // it contains some polyfill
test // it also contains some polyfill
]
};
My .babelrc is:
{
"presets": [
["#babel/preset-env", {
"corejs": {"version": "3", "proposals": false},
"useBuiltIns": "usage"
}
]
],
"plugins": [
[
"#babel/plugin-transform-runtime"
]
]
}
Now, api and test contain some polyfill. Is there a way to extract the used polyfills of all modules to form a single umd file but not Full Function polyfill.Thank you.

Related

Library built with Webpack 5 not tree shakeable

I have a React component library that is built by webpack 5.75.0. The library exports 2 components:
// src/index.ts
export { default as ComponentA } from "components/ComponentA";
export { default as ComponentB } from "components/ComponentB";
A simple CRA app uses this library and imports only ComponentA. But when analyzing the bundle I see that ComponentB's dependencies are in it and have not been tree-shaken.
The parent app is just a CRA app with no config overrides. Tree shaking works there for other modules (MUI, lodash-es, etc), so the problem is with the my library.
I seem to have followed all requirements: in my package.json I do have
"sideEffects": false,
"main": "lib/index.js",
"module": "lib/index.js",
Here's my babel config:
module.exports = {
presets: [
["#babel/preset-env", { targets: { node: "current", modules: false } }],
"#babel/preset-typescript",
[
"#babel/preset-react",
{
runtime: "automatic",
},
],
],
plugins: [
"#babel/plugin-syntax-jsx",
[
"#babel/plugin-transform-runtime",
{
regenerator: true,
useESModules: true,
},
],
],
};
And here's my webpack config:
const config = {
mode: "production",
devtool: false,
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "lib"),
filename: "index.js",
module: true,
library: {
type: "module",
},
},
plugins: [
//...
],
module: {
rules: [
//...
],
},
resolve: {
//...
},
externals: [
"react",
"react-dom",
],
experiments: {
outputModule: true,
},
optimization: {
minimize: false,
},
};
But still, the ComponentB's dependencies are showing up in the bundle of the CRA app that uses this library.
Am I missing something? Is there some official example of how to configure tree-shaking library with webpack 5?
Thanks.
P.S. My question relates to this question, but since 2021 it's become possible to build ES modules with Webpack 5.

In Rollup, create ESM module with no babel transpiling except Flow

I'm trying to release a library that contains Flow syntax in several forms, using Rollup and Babel:
UMD, ES5 (works)
UMD, ES5, minified (works)
ES module (problem)
Specifically, I want the ES module version to be as minimally transpiled as possible: it should use the full range of ES6 features just as I wrote them. I can't seem to stop Babel transpiling down to ES5 though - it creates an _objectSpread2() function and replaces every spread operator with it.
The only reason I want to use Babel is for the Flow syntax. Specifically, converting class property syntax:
class Foo {
myProp = 1;
}
(The standalone tool flow-remove-types does not transform class syntax, so its output is not valid JS).
This is the Rollup config that currently over-transpiles, to ES5:
import { nodeResolve } from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import flow from 'rollup-plugin-flow';
export default [
{
input: 'src/index.js',
output: [
{
file: 'dist/index.esm.js',
format: 'esm', // ES2015 modules version so consumers can tree-shake
},
],
plugins: [flow(), commonjs(), nodeResolve()],
},
];
Even when I remove the presets, it still does exactly the same thing:
import babel from '#rollup/plugin-babel';
export default [
{
input: 'src/index.js',
output: [
{
file: 'dist/index.esm.js',
format: 'esm', // ES2015 modules version so consumers can tree-shake
},
],
plugins: [
babel({
presets: [],
}),
],
},
];
Do I need to change something in .babelrc? It is currently as follows:
{
"presets": [
[
"#babel/preset-env",
{
"targets": {
"esmodules": true
}
}
],
["#babel/preset-flow"]
]
}
The library is here.
Hmm, I think the problem was that .babelrc was being applied even in the ESM case. So the answer is:
Remove the .babelrc file
Move its configuration to rollup.config.js
Configure rollup-esm.config.js to explicitly handle class syntax.
Rollup.config.js:
...
plugins: [
flow(),
commonjs(),
nodeResolve(),
babel({
babelHelpers: 'bundled',
presets: [
[
'#babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
['#babel/preset-flow'],
],
}),
],
rollup-esm.config.js:
import babel from '#rollup/plugin-babel';
export default [
{
input: 'src/index.js',
output: [
{
file: 'dist/index.esm.js',
format: 'esm', // ES2015 modules version so consumers can tree-shake
},
],
plugins: [
babel({
plugins: ['#babel/plugin-proposal-class-properties'],
presets: ['#babel/preset-flow'],
}),
],
},
];

How to configure Storybook to run from a directory other than the project root

I'm trying to configure Storybook to run from a directory that is not the root of the project and I'm having a little trouble. I've setup a mono-rep using https://github.com/jibin2706/cra-monorepo-demo as base.
My project directory looks like this:
- project
-- packages
---- app
---- components
---- utils
---- stories
------ .storybook
-------- main.js
------ ComponentA
-------- ComponentA.stories.mdx
Because I'm using a monorep with aliases (e.g. a component can import from #project/utils) I've configured webpack in .storybook/main.js to read like:
const path = require('path');
module.exports = {
stories: ['../**/*.stories.mdx', '../../**/*.stories.#(js|jsx|ts|tsx)'],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'#storybook/preset-create-react-app',
],
webpackFinal: async (config, { configType }) => {
const result = {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'#project/components': path.resolve(
process.cwd(),
'packages/components'
),
},
},
};
},
};
Then within my ComponentA.stories.mdx I have an import like import { ComponentA } from '#project/components';
When I run this however, I'm always hitting an error when it encounters JSX within a .js file:
ERROR in ./packages/components/MyComponent1/MyComponent1.js 106:11
Module parse failed: Unexpected token (106:11)
File was processed with these loaders:
./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
You may need an additional loader to handle the result of these loaders.
|
return <React.Fragment>{children}</React.Fragment>;
I can't seem to work out why this error is throwing. I've tried running with yarn storybook --debug-webpack which seems to include a loader for both jsx and js files. I'm not 100% sure if this is correct, but it looks roughly right from other docs I've read.
module: {
rules: [
{
test: /\.(mjs|tsx?|jsx?)$/,
use: [
{
loader: '/home/ian/src/cra-monorepo-demo/node_modules/babel-loader/lib/index.js',
options: {
sourceType: 'unambiguous',
presets: [
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-env/lib/index.js',
{ shippedProposals: true, loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-typescript/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-react/lib/index.js'
],
plugins: [
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-shorthand-properties/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-block-scoping/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-decorators/lib/index.js',
{ legacy: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-class-properties/lib/index.js',
{ loose: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-private-methods/lib/index.js',
{ loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-export-default-from/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-syntax-dynamic-import/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-object-rest-spread/lib/index.js',
{ loose: true, useBuiltIns: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-classes/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-arrow-functions/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-parameters/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-destructuring/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-spread/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-for-of/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#storybook/core-common/node_modules/babel-plugin-macros/dist/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-optional-chaining/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-nullish-coalescing-operator/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/babel-plugin-polyfill-corejs3/lib/index.js',
{
method: 'usage-global',
absoluteImports: '/home/ian/src/cra-monorepo-demo/node_modules/core-js/index.js',
version: '3.16.1'
}
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-template-literals/lib/index.js'
]
}
}
],
include: [ '/home/ian/src/cra-monorepo-demo' ],
exclude: [ /node_modules/, /dist/ ]
},
{
test: /\.js$/,
use: [
{
loader: '/home/ian/src/cra-monorepo-demo/node_modules/babel-loader/lib/index.js',
options: {
sourceType: 'unambiguous',
presets: [
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-env/lib/index.js',
{
shippedProposals: true,
modules: false,
loose: true,
targets: 'defaults'
}
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-react/lib/index.js'
],
plugins: [
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-shorthand-properties/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-block-scoping/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-decorators/lib/index.js',
{ legacy: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-class-properties/lib/index.js',
{ loose: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-private-methods/lib/index.js',
{ loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-export-default-from/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-syntax-dynamic-import/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-object-rest-spread/lib/index.js',
{ loose: true, useBuiltIns: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-classes/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-arrow-functions/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-parameters/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-destructuring/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-spread/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-for-of/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#storybook/core-common/node_modules/babel-plugin-macros/dist/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-optional-chaining/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-nullish-coalescing-operator/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/babel-plugin-polyfill-corejs3/lib/index.js',
{
method: 'usage-global',
absoluteImports: '/home/ian/src/cra-monorepo-demo/node_modules/core-js/index.js',
version: '3.16.1'
}
]
]
}
}
],
include: [Function: include]
},
...
Can anyone see what I might be missing here, or what additional config is required?
The issue is probably with the Storybook project root. The default babel-loader defines an include that is equal to the project root. And the "project root" is usually the closest .git folder.
A workaround is to set the correct project root:
const path = require("path");
module.exports = {
// ...
webpackFinal: async (config, { configType }) => {
const babelLoaderRule = config.module.rules.find(
(rule) => rule.test.toString() === /\.(mjs|tsx?|jsx?)$/.toString()
);
// set correct project root
babelLoaderRule.include = [path.resolve(__dirname, "../..")];
return config;
}
};
What "correct" path is, depends on your setup.
Check my post for a longer write-up.
As far as I can tell your .storybook/main.js looks fine.
The built-in loader should also work as expected.
Concerning the error you're seeing: have you tried changing the file-type (aka renaming the file ending) from .js to .jsx?
Because the loader can discern between .js and .jsx files, however the interpreter cannot comprehend jsx-notation if it's not explicitely told to do so (as is the case with your .js files).

module is not defined at node_modules/core-js/internals/global.js

I'm building a library in TypeScript, and I'm trying to test it. As It involves browser API I'm testing it using #web/test-runner and chai.
I'm using TypeScript, and some libraries are commonjs modules, so I'm using Rollup to bundle all of that into something the browser can use.
My rollup config is as follow:
import typescript from '#rollup/plugin-typescript';
import commonjs from '#rollup/plugin-commonjs';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import json from '#rollup/plugin-json';
import sourceMaps from 'rollup-plugin-sourcemaps';
import { babel } from '#rollup/plugin-babel';
import nodePolyfills from 'rollup-plugin-node-polyfills';
const pkg = require('./package.json');
const libraryName = 'AGreatLibrary';
export default {
input: 'src/index.ts',
output: [
{ file: pkg.main, name: libraryName, format: 'umd', sourcemap: true },
{ file: pkg.module, format: 'es', sourcemap: true },
],
plugins: [
json(),
nodePolyfills(),
typescript(),
commonjs(),
nodeResolve({ browser: true, preferBuiltins: false }),
sourceMaps(),
babel({ babelHelpers: 'bundled', exclude: [/\bcore-js\b/, /\bwebpack\/buildin\b/] }),
],
};
My babel config
{
"presets":
[
[
"#babel/env",
{
"targets":
{
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3"
}
]
],
"minified": true
}
My test file
import { Client } from "../lib/index.es.js";
import { expect } from '#esm-bundle/chai';
it('Should create a Client', () => {
let client = new Client("123", true);
expect(client.accessToken).to.equal("123");
});
it('Should auth', async () => {
let client = new Client("123", true);
let resp = await client.tryAuth();
expect(resp.ok).to.be.true;
});
But when I run my test (web-test-runner "test/**/*.test.ts" --node-resolve)
I get the following error
🚧 Browser logs:
ReferenceError: module is not defined
at node_modules/core-js/internals/global.js:6:0
I have no idea what to do.
I fix it!
plugins: [
commonjs(),
json(),
nodePolyfills(),
typescript(),
nodeResolve({ browser: true, preferBuiltins: false }),
sourceMaps(),
babel({ babelHelpers: 'bundled', exclude: [/\bcore-js\b/, /\bwebpack\/buildin\b/] }),
],
commonjs should be on top of the array.

Jest won't transform node_module dependencies

Our code references the #myScope/vue-notify library which is set as a external/global in our rollup config. That code is an ES6 module and needs to be transpiled for jest to be able to work with it but by default Jest doesn't transpile node_modules folder. I have tried adding a negative lookahead to the transformIgnorePatterns but no matter what combination I try I just keep getting this error:
My Jest transform settings are as follows:
transform: {
'.+\\.(css|css!|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
'^.+\\.(js|jsx)?$': 'babel-jest',
},
transformIgnorePatterns: [
'/node_modules/(?!#myScope/vue-notify)',
'/dist/',
'/docs/',
],
My rollup.config.js is as follows:
import packageJson from './package.json';
import json from '#rollup/plugin-json';
import babel from '#rollup/plugin-babel';
import { addMinExtension } from './utils/utils';
import { terser } from 'rollup-plugin-terser';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
const globals = {
DevExpress: 'DevExpress',
jquery: '$',
'#myScope/vue-notify': 'vueNotify',
'file-saver': 'saveAs',
exceljs: 'ExcelJS',
};
const externals = ['jquery', '#myScope/vue-notify', 'file-saver', 'exceljs'];
export default [
{
// Dev UMD Config:
input: packageJson.input,
output: [
{
name: packageJson.moduleName,
file: packageJson.browser,
format: 'umd',
globals: globals,
},
],
external: externals,
plugins: [
nodeResolve(),
commonjs({
include: 'node_modules/**',
}),
json(),
babel({ babelHelpers: 'runtime' }),
],
},
{
// Dev Module Config:
input: packageJson.input,
output: [
{
file: packageJson.module,
format: 'es',
},
],
external: externals,
plugins: [
nodeResolve(),
commonjs({
include: 'node_modules/**',
}),
json(),
],
},
{
// Prod UMD Config:
input: packageJson.input,
output: [
{
name: packageJson.moduleName,
file: addMinExtension(packageJson.browser),
format: 'umd',
sourcemap: true,
globals: globals,
},
],
external: externals,
plugins: [
nodeResolve(),
commonjs({
include: 'node_modules/**',
}),
json(),
babel({ babelHelpers: 'runtime' }),
terser(),
],
},
{
// Prod Module Config:
input: packageJson.input,
output: [
{
file: addMinExtension(packageJson.module),
format: 'es',
},
],
external: externals,
plugins: [
nodeResolve(),
commonjs({
include: 'node_modules/**',
}),
json(),
terser(),
],
},
];
Any help getting this working would be much appriciated!

Categories

Resources