How to import dynamically with webpack - javascript

I want to use FontAwesome's icons, but the whole package is too large and I have to select only the ones that I'm using
I'm using vue & webpack
Right now I have the standard:
import { library } from '#fortawesome/fontawesome-svg-core'
import { faGem as falFaGem, faDatabase as falFaDatabase } from '#fortawesome/pro-light-svg-icons'
import { faGem as farFaGem } from '#fortawesome/pro-regular-svg-icons'
import { faGem as fasFaGem } from '#fortawesome/pro-solid-svg-icons'
library.add(falFaGem, falFaDatabase, farFaGem, fasFaGem)
The thing is I have around 80 (for now) icons and some of them duplicates like faGem in the example, hence the "faGem as farFaGem"
I tried importing FAS from '#fortawesome/pro-regular-svg-icons' and making a foreach and adding to library only the icons that I need but webpack imports the whole package into the compiled code
Is there an easier, cleaner way to achieve this?

I believe that the title to your question is a bit wrong. What you want to achieve is reduce the size of the FontAwesomes npm package and that is something that can be achieved in different ways.
The most common way nowadays is using Treeshaking. Basically, your code will be '''analysed''' and a graph of dependencies will be generated, before giving you the "compiled" version of your code it will remove all those modules that were not used from FontAwesome.
FontAwesome can perform TreeShaking if your tool (webpack) allows it, sadly it seems that there are some problems with Webpack 4 but they offer some work arounds like setting the variable modules false in your config:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: './bundle.js'
},
module: {
rules: [{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', { modules: false }],
],
plugins: ['#babel/plugin-proposal-class-properties'],
}
}
},
]
}
};
In the other hand I believe that what you want to achieve is quite difficult (you will still need to declare all the icons that you want to use from FontAwesome (to use a require(...) call dynamically is something that the tree shaking algorithm can not work around and it will import the whole npm package), and the same for the name conflicts.
If you want it to be a bit cleaner, maybe declare and add all this icons in a separated file of your project, but as far as I can tell, there is no a better way to achieve what you want.

Related

How to transpile simple react component in express workspace

I have a fairly simple question.
I was writing simple express page for company (basic table delete row button and form page)
easyyyy
But I decided to spruce form page a little bit with ajax validation and dynamic fields. Since I did few full react sites before i thought making this component would be easy. And tbh making component is easy but babel and webpack is not.
is there any easy way to transpile jsx with imports to the web without configuring whole separate workspace?
If react for this overkill and jQuery fell out of grace is there any other suitable library?
Hope you can understand my broken English
yes react is powerful, you can add it and setup it like,
you can create the component separately and load the script on your target page just make sure you place the mount point right
and you can use create react app with this as well
just build the project place the bundle js path on the same page
and add the mount point and it should work smoothly
<div id="mountPoint" ></div>
another way like the react does suggest here
I did minimal configuration of webppack and babel. When it wasnt so bad still configuring two libraries for one small component with 2 dependencies seems ridiculous :P
My config maybe somebody finds it useful:
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
output: {
filename: 'public/javascripts/dist.js'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', "#babel/preset-react"]
}
}
}
]
}
};
babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = [];
const plugins = ['#babel/plugin-proposal-class-properties'];
return {
presets,
plugins
};
}

CSS into JS using webpack

I`m trying to paste a CSS Code into a JS File using webpack
My flux is the following
SASS file > CSS content > PostCSS > css file
{
test: /\.(sass|scss)$/,
exclude: /node_modules/,
use: [
MiniCSSExtractPlugin.loader,
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true,
sassOptions: {
outputStyle: 'compressed'
}
}
}
]
}
But the MiniCSSExtractPlugin gets me the content into a css file.
I'm use Lit Element so the styles should be declare with css function part of lit-element on the following way
import {css} from 'lit-element';
export default css`
:host {
display: inline;
}
`;
Is there any way to generate css code as a string and paste it into js file?
To import a CSS file into JS (w/webpack) you need to configure webpack with the following loaders:
css-loader
style-loader
Both available with NPM:
$ npm i css-loader style-loader --save-dev
And add this entry to the module.rules array in your webpack configuration:
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
The result
Both of this loaders correctly configured will allow you to do the following sentence in JavaScript:
import myStyles from './your-css-file.css';
And then, you can just paste them into literal templates as follows:
static get styles() {
return css`${myStyles}`;
}
Additionally:
With a deep knowledge into what you might be talking about, you might need to take a look around #cells/cells-cli files and add those loaders into webpack configuration. Otherwise, you may need to create a webpack.config.js file in every lit-element component, which might not be the best for the current architecture.
Nice to see you around here, ¡saludos!;)
#k3llydev, I tried your suggestion and couldn't get it to work. Do you have any suggestions, specifically when you say "both of these loaders correctly configured will allow you to do the following"? I'd like to be able to import the CSS and then use it directly in the styles getter like you show in your example, but I had to do this as a workaround:
import MyImportedStyle from './some.css';
static get styles () {
return [
css`${unsafeCSS(MyImportedStyle.toString())}`
];
}
While using the 'to-string-loader' in webpack:
{
test: /\.css$/i,
use: ['to-string-loader', 'css-loader'],
}
This worked out for me and did what I wanted, but if I could avoid using the to-string-loader and could use the imported object directly, that would be idea. Any suggestions?
This way should do what the original poster asked for, a way to get the CSS as a string and use it in your LitElement.

Minify/Mangle CSS Classnames in JSX and CSS output from Webpack (or other programmatic means)

I'd like to minify my classnames (for very minimal source protection purposes) in both my output CSS files and in the rendered JSX from my React components similarly to this Webpack plugin: https://github.com/vreshch/optimize-css-classnames-plugin
Is there any existing option I can use to achieve this, either Webpack or otherwise? Thanks very much.
From:
<div className="long-class-name"></div>
.long-class-name {
}
To:
<div class="a"></div>
.a {
}
As you're already using Webpack, I think one good option is to use CSS Modules to accomplish that. You can use either css-loader or postcss-modules to do that, for example.
Basically, by using CSS Modules, you can import your CSS and treat it as a JSON. So, if you write .long-class-name { } you'll have something like this { 'long-class-name': '<<interpolated name>>' }. The trick here is that the <<interpolated name>> in my example is something you can set programmaticaly.
Webpack has some predefined tokens that you can use, as you can see here: https://github.com/webpack/loader-utils#interpolatename. And you can check an example here:
{
test: /\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}
]
}
However, if you want something more "customized", you can specify a getLocalIdent function:
{
test: /\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]',
getLocalIdent: (context, localIdentName, localName, options) => {
return 'whatever_random_class_name';
}
}
}
]
}
Please, refer to the docs to read more about the options on CSS Modules.
Doing this way, you can specify your class names the way you need and solve your problem.
Hope that helps!
For anyone wanting to easily mangle classnames in Next.js, use my package!

Webpack fast import rule

I need to use webpack with snapsvg. According to snapsvg's documentation, to import it with webpack, I need to use a loader:
const Snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);
So, in every Javascript file I use, I need to write this thing. I would like to avoid it.
Aliased import
Is there a way, maybe in webpack.config.js, to specify that every time this is encoutered:
import snap from "snapsvg"
Then that line is translated into the one mentioned before? So I can avoid typing the same thing in every file.
You can use this rule:
Install Imports Loader (npm i -D imports-loader) and add this to your webpack config:
module: {
rules: [
{
test: require.resolve('snapsvg/dist/snap.svg.js'),
use: 'imports-loader?this=>window,fix=>module.exports=0',
},
],
},
resolve: {
alias: {
snapsvg: 'snapsvg/dist/snap.svg.js',
},
},
With that you can import snap from "snapsvg" without hassle.

How To Use ScrollMagic with GSAP and Webpack

In order to use ScrollMagic with GSAP, you need to load the animation.gsap.js plugin. With Webpack you would do something like this to accomplish that (assuming you use the CommonJS syntax and installed everything with npm):
var TweenMax = require('gsap');
var ScrollMagic = require('scrollmagic');
require('ScrollMagicGSAP');
To make sure that this actually works, you have to add an alias to your Webpack configuration, so that Webpack knows where the plugin lives.
resolve: {
alias: {
'ScrollMagicGSAP': 'scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap'
}
}
Unfortunately, ScrollMagic keeps throwing an error, when you are using this configuration and the CommonJS syntax like above.
(ScrollMagic.Scene) -> ERROR calling setTween() due to missing Plugin 'animation.gsap'. Please make sure to include plugins/animation.gsap.js
The Solution
You have to tell Webpack to stop using the AMD syntax by adding the following loader that deactivates the define() method.
// Webpack 4+
module: {
rules: [
{ parser: { amd: false }}
]
}
// Webpack <= 3
// Don’t forget to install the loader with `npm install imports-loader --save-dev`
module: {
loaders: [
{ test: /\.js$/, loader: 'imports-loader?define=>false'}
// Use this instead, if you’re running Webpack v1
// { test: /\.js$/, loader: 'imports?define=>false'}
]
}
Why?
The problem lies in the fact that Webpack supports the AMD (define) and CommonJS (require) syntax. That is why the following factory script within plugins/animation.gsap.js jumps into the first if statement and fails silently. That is why setTween() etc. are never added to the ScrollMagic Constructor.
By telling Webpack not to support the AMD syntax (using the loader mentioned above), the plugin jumps into the second if statement correctly, embracing the CommonJS syntax.
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['ScrollMagic', 'TweenMax', 'TimelineMax'], factory);
} else if (typeof exports === 'object') {
// CommonJS
// Loads whole gsap package onto global scope.
require('gsap');
factory(require('scrollmagic'), TweenMax, TimelineMax);
} else {
// Browser globals
factory(root.ScrollMagic || (root.jQuery && root.jQuery.ScrollMagic), root.TweenMax || root.TweenLite, root.TimelineMax || root.TimelineLite);
}
I hope this prevents other people from spending a whole evening trying to figure out what is going on.
The solution I came across that doesn't require you to alter your webpack.config.js file and actually works for me can be found here: https://github.com/janpaepke/ScrollMagic/issues/665
The gist of it is to make sure you have ScrollMagic and GSAP added via npm (hopefully that's obvious) as well as imports-loader:
npm install --save scrollmagic gsap
npm install --save-dev imports-loader
Then in the file you want to use ScrollMagic with GSAP do the following imports:
import { TimelineMax, TweenMax, Linear } from 'gsap';
import ScrollMagic from 'scrollmagic';
import 'imports-loader?define=>false!scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap';
Using Webpack 4.x and imports-loader 0.8.0
medoingthings solution has since changed syntax to include "-loader" suffix.
module: {
loaders: [
{ test: /\.js$/, loader: 'imports-loader?define=>false'}
]
}
https://webpack.js.org/guides/migrating/#automatic-loader-module-name-extension-removed
In imports-loader 1.1.0, the syntax of the configuration has changed a bit, so now you have to use the following to get the ScrollMagic plugins to work:
{
test: [
path.join(config.root, '/node_modules/scrollmagic/scrollmagic/uncompressed/plugins/jquery.ScrollMagic.js'),
path.join(config.root, '/node_modules/scrollmagic/scrollmagic/uncompressed/plugins/debug.addIndicators.js')
],
use: [
{
loader: 'imports-loader',
options: {
additionalCode: 'var define = false;'
}
}
]
}
Hopefully this helps others.
I was having the same issue and found this question.
For those using Webpack 5 I believe imports-loader is out of date so according to the webpack docs add this code to your js rule to disable AMD:
{
test: /\.js$/,
include: /node_modules/,
parser: {
amd: false
}
}
documentation: https://webpack.js.org/configuration/module/#ruleparser

Categories

Resources