Treeshake bootstrap CSS with rollup(plugin) - javascript

I am trying to remove unused CSS from my code, but I am struggling with tree-shaking my CSS files.
I decided to use rollup instead of webpack, because it was said that tree-shaking is a core idea behind the library, but now it seems to me that rollup is not capable of tree-shaking CSS files at all.
What I am doing is the following:
I create a rollup config for my page:
export default [
{
input: "path/to/index.js",
output: [
{
format: "esm",
file: "path/to/output/index.min.js",
},
],
treeshake: true,
plugins: [
postcss({
extract: true,
plugins: [cssnano()],
extensions: [".css"],
}),
resolve(),
commonjs(),
terser(),
],
},
Then I have my index.js file where I import my CSS and my JS:
import "../path/bootstrap.css";
import "../path/css/nav.css";
import "../path/css/footer.css";
import "bootstrap/js/dist/carousel";
import "../modules/css/example.css/";
import { example_function } from "../path/to/js/example.js";
example_function();
I run the rollup bundler and it creates a minified JS and a minified CSS file. So far so good, but it still includes the complete bootstrap classes for the CSS. I would like to tree-shake the CSS and only include the classes that are actually used.
Are there some settings that I am missing? Or is this not as easyly possible as I hope to achieve it.

Related

Problems with bundling bootstrap in rollup

I am trying to bundle my stylesheets which should use bootstrap v5. I can bundle my CSS files without a problem but I cannot get bootstrap to work. The bundler runs through, but bootstrap styles are not included.
What I did:
I installed bootstrap with npm install bootstrap (https://getbootstrap.com/docs/5.0/getting-started/download/#npm)
I included bootstrap into:
either my JS file with import 'bootstrap';
in my CSS with #import "node_modules/bootstrap/dist/css/bootstrap"; (This is not working because it tells me that my file is not found)
My rollup.config:
import resolve from "#rollup/plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import { terser } from "rollup-plugin-terser";
import postcss from "rollup-plugin-postcss";
import cssnano from "cssnano";
export default [
{
input: "project/static/src/inputs/index.js",
output: [
{
format: "esm",
file: ""project/static/src/outputs/index.min.js",",
},
],
plugins: [
postcss({
extract: true,
plugins: [cssnano()],
extensions: [".css"],
}),
resolve({
jsnext: true,
browser: true,
}),
commonjs(),
terser(),
],
},
];
This config creates a separate CSS file because I set extract = true for the postcss plugin.
Then, in my CSS file:
#import "../modules/css/example.css";
#import "node_modules/bootstrap/dist/css/bootstrap";
.container-example {
margin-top: -1.5rem;
}
In my JS
import "./example.css";
import 'bootstrap';
//more JS here
I then run the bundler and it bundles my JS and also my styles. But it does not include any bootstrap styles... I can't seem to figure out why. Maybe because BS is all about SASS? Any ideas? Help is very much appreciated.

React Rollup: 'name' is not exported by node_modules/

I am trying to make a library/package from my component.
Tech-stack is: React, Typescript... and a bunch of other dependencies.
I am using Rollup and when I try to build the package I get the following error:
[!] Error: 'DisplayHint' is not exported by
../node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js,
imported by ../src/utils/answerTypeConversions.ts
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
Rollup:
import babel from 'rollup-plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import external from 'rollup-plugin-peer-deps-external';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import reactSvg from 'rollup-plugin-react-svg';
import typescript from 'rollup-plugin-typescript2';
import svg from 'rollup-plugin-svg';
import commonjs from '#rollup/plugin-commonjs';
import json from '#rollup/plugin-json';
import replace from '#rollup/plugin-replace';
import urlResolve from 'rollup-plugin-url-resolve';
export default [
{
input: './src/index.ts',
output: [
{
file: './dist/index.js',
format: 'cjs',
sourcemap: true,
},
{
file: './dist/index.es.ts',
format: 'es',
exports: 'named',
sourcemap: true,
},
],
plugins: [
typescript({
tsconfig: './tsconfig.build.json',
verbosity: 3,
clean: true,
check: true,
}),
babel({
exclude: ['/node_modules'],
presets: [
'#babel/preset-react',
'#babel/preset-flow',
'#babel/preset-env',
],
extensions: ['.ts', '.tsx'],
}),
commonjs({
include: './node_modules',
dynamicRequireTargets: [
// include using a glob pattern (either a string or an array of strings)
'/node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js',
'./node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js',
],
}),
resolve({
browser: true,
preferBuiltins: true,
mainFields: ['browser'],
}),
urlResolve(),
postcss({
plugins: [],
minimize: true,
}),
external(),
terser(),
reactSvg({
jsx: false,
include: ['custom.d.ts'],
exclude: null,
}),
svg(),
replace({
include: ['../src/icons/**'],
preventAssignment: true,
// Replace ReactComponent to allow resolution of SVG files under Rollup
ReactComponent: 'default',
}),
json(),
],
},
];
DisplayHint:
/**
* An indication of how clients should display a question.
*
* This is inferred from the answer_format and answer_display api properties.
* Those properties should not be used directly, and clients should rely instead
* solely on DisplayHint.
*/
export declare const DisplayHint: {
readonly ButtonGroup: "DisplayHint::ButtonGroup";
readonly Checkbox: "DisplayHint::Checkbox";
};
export declare type DisplayHint = typeof DisplayHint[keyof typeof DisplayHint];
Looks like DisplayHint is a TS type/interface, not an exported JS value.
Rollup per se is a bundler not a TS language analyzer. It cannot tell if a named export is a concrete JS value or merely a non-existing TS type, thus the reported error.
Rollup plugin order matters. To resolve this specific problem, just lift the typescript plugin up in order, at least before babel. TS plugin, if put to work first, will correctly erase TS type from JS code output.
Update
I know another trick, but it’s a workaround-ish one. The trick is to use import type syntax to explicitly mark DisplayHint as a TS type.
import type { DisplayHint } from "#bestowinc/enroll-sdk-core"
import { otherNonTypeStuff } from "#bestowinc/enroll-sdk-core"
This trick is not satisfying because it requires you to explicitly mark everywhere you need a TS type, and you have to separate concrete JS exports from TS type ones, like shown above. This is fine if it’s a one-off thing but if you have a large codebase it’s very tedious.
I have to add, it’s unexpected to me that original solution doesn’t work. My guess is that this module #bestowinc/enroll-sdk-core is somehow quirky. (Why you include them as dynamic import target in the config anyway?)
It’s a JS module with .d.ts annotation, that I can tell, but looks like it’s a private module, I cannot see the code thus cannot dive deeper. My guess is that .d.ts declaration is broken, it doesn’t match up to the real export of JS code.
Anyway, I hope this trick resolve your problem. If you need me to look closer, I’ll have to ask you to setup a minimal reproducible example. With current info there’s not much I can do.
I've had similar issue just happening with linked library (with typescript plugin in place), in my case solution was to add the linked library to config 'external' property. But node_modules should be treated as external anyway so not sure if this will apply

CSS Module gets bundled but is not referenced using TSDX which uses Rollup underhood

I have created a React library using TSDX → https://github.com/deadcoder0904/react-typical
It uses CSS Modules & attaches styles to the React library.
The bundle correctly outputs CSS file into the dist/ folder but it never really references it as it should.
This causes my styles to not show up at all.
Here's my complete tsdx.config.js:
const postcss = require('rollup-plugin-postcss');
const { terser } = require('rollup-plugin-terser');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
module.exports = {
rollup(config, options) {
config.plugins.push(
postcss({
modules: true,
plugins: [
autoprefixer(),
cssnano({
preset: 'default',
}),
],
inject: false,
// only write out CSS for the first bundle (avoids pointless extra files):
extract: !!options.writeMeta,
minimize: true,
}),
terser()
);
return config;
},
};
If you see the dist/ folder at https://yarnpkg.com/package/#deadcoder0904/react-typical?files, then you'll notice it has index.js file but it doesn't reference the .css file at all.
Same thing with every .js file in dist/ folder. No file references .css file & the CSS code isn't bundled at all so it just doesn't use styles.
How do I solve this?
From my understanding, normally a library usually introduce the component & styles separately and let the users know in the document if they want to use default style then let import css file too such as:
import ReactTypical from "react-typical"
import "react-typical/dist/react-typical.cjs.development.css"
That is as same as your case I guess
Or you would set your style by default without asking them to import manually which means you have to refine your config by setting inject: true, it will import your css context into your js file then execute at runtime to append the script in the <head /> of the document. Then your changing config would look like:
postcss({
modules: true,
plugins: [
autoprefixer(),
cssnano({
preset: 'default',
}),
],
// Append to <head /> as code running
inject: true,
// Keep it as false since we don't extract to css file anymore
extract: false,
})
In order to work with storybook, add following css rules to your webpack storybook config ./story/main.js:
// Remove the existing css rule
config.module.rules = config.module.rules.filter(
f => f.test.toString() !== '/\\.css$/'
);
config.module.rules.push(
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
],
include: path.resolve(__dirname, "../src"),
}
)
But you miss them in your package dependency, just remember to add them:
npm i -D style-loader css-loader

How to include both import and require statements in the bundle using rollup

When I use only const Example1 = require('./example1.js) statement then code inside example1.js file is getting included in the bundle. And if I use only import Example2 from './example2.js' then also code inside example2.js is getting included in the bundle. But if I use both the statements only import is working and require is not working.
I am using rollup for bundling.
My rollup configuration looks like this
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
import svg from 'rollup-plugin-svg'
import json from 'rollup-plugin-json';
import { terser } from 'rollup-plugin-terser'
export default {
input: 'src/sdk/test.js',
output: [
{
file: "src/sdk/sdk.js",
format: 'cjs'
},
{
file: "src/sdk/sdk.es.js",
format: 'es'
},
{
file: "src/sdk/sdk.iife.js",
format: 'iife'
}
],
plugins: [
resolve({
browser: true,
}),
commonjs(),
external(),
postcss({
modules: true
}),
url({
limit: 100 * 1024,
emitFiles: false
}),
svg(),
babel({
exclude: 'node_modules/**',
"plugins": ["#babel/plugin-proposal-class-properties"]
}),
terser(),
json()
]
}
By default, rollup supports 'esm' modules as entry. If you like to include commonjs files in the project, you can include '#rollup/plugin-commonjs' into plugins field, it usually works.
Maybe the following config will help, I tried with very simple example:
commonjs({transformMixedEsModules:true})
transformMixedEsModules
Instructs the plugin whether or not to enable mixed module transformations. This is useful in scenarios with mixed ES and CommonJS modules. Set to true if it's known that require calls should be transformed, or false if the code contains env detection and the require should survive a transformation.
https://github.com/rollup/plugins/tree/master/packages/commonjs

What causes failure to compile SASS in this svelte applcation?

I am working on a project with Svelte and the material design library Svelte Material UI.
This material design library requires SASS, so I installed a preprocessor with npm install svelte-preprocess and added preprocess: autoPreprocess() in rollup.config.js. So I now have:
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/bundle.css');
},
preprocess: autoPreprocess()
}),
routify({ singleBuild : true}),
replace({
// stringify the object
APPENV: JSON.stringify({
isProd: production,
...config().parsed // attached the .env config
}),
}),
// more stuff
]
I have a file smui.js with this content:
import Button from '#smui/button';
import Checkbox from '#smui/checkbox';
import Chips from '#smui/chips';
import Dialog from '#smui/dialog';
import FormField from '#smui/form-field';
import Select from '#smui/select';
export {
Button,
Checkbox,
Chips,
Dialog,
FormField,
Select
}
In my index.svelte file I am importing the above this way: import * as Smui from "../smui.js";.
Instead of a success message with the port on which the app should run, I get:
[!] Error: Unexpected character '#' (Note that you need plugins to import files that are not JavaScript)
node_modules\#smui\dialog\_index.scss (1:0)
1: #import "smui-theme";
^
2: #import "./style";
Error: Unexpected character '#' (Note that you need plugins to import files that are not JavaScript)
What am I doing wrong?
I had the same issue and somehow I managed to fix this with rollup-plugin-postcss plugin. Update your rollup.config.js with the following code and you should have _smui-theme.scss in your one of sass directories.
import postcss from 'rollup-plugin-postcss'
...
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/bundle.css')
}
}),
postcss({
extensions: ['.css'],
extract: true,
minimize: true,
use: [['sass', { includePaths: ['./src/(yoursass-directory-name)', './node_modules'] }]]
})
I've never used #import to import components from a NPM package, but at the readme package you're referencing it recommends using 'import x from" svelte-material'. Also pay attention that svelte-preprocess won't be supported by the package you're referencing, take a look at the readme:
To bundle this in your own code, use a Sass processor (not a Sass Svelte preprocessor, but a Sass processor).

Categories

Resources