Change default Laravel Mix Minimizer/Minifier (webpack) - javascript

Is there a way to change the default minifier that Laravel Mix uses?
By default, it uses 'Terser' (the Webpack default), but I would like it to instead use Closure Compiler (see here).
I have tried various things but have not had any luck yet. This is my latest failed attempt:
mix.webpackConfig({
module: {
rules: [
// ...
]
},
plugins: [
// ...
],
resolve: {
alias: {
// ...
}
},
optimization: {
minimizer: [
new ClosurePlugin({mode: 'STANDARD'}, {})
]
}
});
I would like to specifically achieve this using the webpack.mix.js configuration if possible i.e. I would like to avoid overriding the Laravel mix configuration (this may not be possible)

Related

Rollup UMD Module Reference to Named Imports is Undefined

I had a published component library (my-components) that had a specific component (Compare) in it, along w/ other components and utilities that were used in a main application. We use rollup and create several different output formats, one of which is UMD (required by one of the teams using my-components).
The component (Compare) was starting to become large, so it was moved out on its own. The rollup build was based off of what was used for my-components. After moving it out to its own repository to be built as its own component, it now had a dependency on my-components. When used in the main application (CRA app), everything works as expected. However, when the team who uses the UMD module tried to use the Compare app, they started to get errors:
Cannot read properties of undefined (reading someService)
The code in Compare is:
import {someService, somethingElse} from 'my-components';
. . .
someService.doSomething();
I saw the following warning during the build:
WARNING: { code: 'MISSING_GLOBAL_NAME', guess: 'myComponents', message: 'No name was provided for external module \'my-components\' in output.globals - guessing \'myComponents\'' }
So I explicitly added a global entry for it.
When I looked at the resulting code in the UMD module, the code looked like:
myComponents.someService.doSomething();
Why doesn't myComponents.someService resolve to the class that is exported from my-components?
My rollup.config.js looks like:
const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.json'];
const umdGlobals = {
axios: 'axios',
lodash: '_',
react: 'React',
'react-dom': 'ReactDOM',
'my-components': 'myComponents'
};
export default [
{
input: 'src/components/index.js',
output: [
{ file: pkg.module, format: 'esm', sourcemap: true },
{ file: pkg.main, format: 'cjs', sourcemap: true },
{ file: pkg.umd, name: 'Compare', format: 'umd', sourcemap: true, globals: umdGlobals }
],
plugins: [
autoExternal(),
resolve({ extensions: EXTENSIONS, preferBuiltins: false, browser: true}),
commonjs({include: ['node_modules/**']),
babel({
babelHelpers: 'bundled',
babelrc: false,
presets: ['#babel/preset-env', '#babel/preset-react', '#babel/preset-typescript'],
plugins: ['#babel/plugin-transform-arrow-functions', '#babel/plugin-proposal-object-rest-spread', '#babel/plugin-proposal-class-properties],
extensions: EXTENSIONS,
exclude: 'node_modules/**'
}),
json(),
requireContext(),
internal(['classnames', 'pluralize'])
]
}
];
I tried to put my-components inside of internal(), and remove it from the global list, but that started to give other warnings that I think were the result of a dependency inside of it, and would throw errors when the bundle was used anyway.
Any advice on what I may be missing? Is there something wrong w/ the output from the my-components that I should be trying to fix in its rollup config, or is there something I can do in my rollup config for Compare?
Any help is greatly appreciated.

Can't import the named export 'Directive' from non EcmaScript module (only default export is available)

this is a ionic angular project that i'm working on, i'm using ng-lazyload-image plugin Link. when i start compiling it showing errors like this.
Error: ./node_modules/ng-lazyload-image/fesm2015/ng-lazyload-image.mjs 401:10-19
Can't import the named export 'Directive' from non EcmaScript module (only default export is available)
This means your bundler resolves .mjs files, however it doesn't know that they are ESM modules. On webpack, you can add the following to rules.
webpack.config.js (in project root)
module.exports = {
configureWebpack: {
module: {
rules: [
{
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
}
]
}
}
}
https://webpack.js.org/configuration/
EDIT: Found a solution for craco.config.js
module.exports = {
webpack: {
configure: (webpackConfig) => {
webpackConfig.module.rules.push({
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
});
return webpackConfig;
},
plugins: [
// Inject the "__DEV__" global variable.
new webpack.DefinePlugin({
__DEV__: process.env.NODE_ENV !== "production",
})
],
},
};
The answer of #Joosep.P works, but for someone with laravel and webpackmix the following is the way to go. In webpack.mix.js file add the following:
mix.webpackConfig({
module: {
rules: [
{
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
}
]
}
});
Just posting it as another answer as it may help someone else or me to find the solution with laravel and webpackmix easily in the future. Thanks.
It probably has to do with different Angular versions.
If ng-lazyload-image is using Angular 13 and your own project is using a lower version this will happen. There are some breaking changes since Angular 13.
If ng-lazyload-image is using Angular 13 there are no es2015 files generated for it's npm package and your compiler is still looking for them.
An option to solve this would be to use a lower version of the ng-lazyload-image package or update your own Angular to Angular 13+

Override some Webpack 5 NodeJS modules with polyfill

I have NodeJS code that I now need to move to an embedded system. It takes far too long to simply start NodeJS ("Hello World" ~11sec on a BeagleBone Black) so we needed an alternative. The IoT.js looks promising but it does not support some of the internal NodeJS modules (e.g. url, zlib, tty)--which my code needs. I am using Webpack 5.35.0 to create a single file for my code but this is where my problems lie. I want to use Webpack with a node target since IoT.js offers most of what node offers natively. However is there a way to force Webpack to use polyfills for some of the modules? For example, browserify-zlib instead of expecting nodes zlib.
My basic Webpack configuration is simple:
{ target: 'node10.17',
entry: './index.js',
output:
{ filename: 'index.js',
path: '/work/proj/dist',
libraryTarget: 'umd' },
stats: 'errors-only',
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
}
}
I have done some reading where people claim adding a simple resolve.fallback.zlib = false and resolve.alias should do the trick--which is not working for me.
I tried to simply add resolve.fallback.zlib = false in the hopes to just have zlib omitted from the Webpacked output and this did not work. No matter what I do the standard Webpack boilerplate "node" zlib include code exists.
Standard Webpack boilerplate when using node target.
/***/ "zlib":
/*!***********************!*\
!*** external "zlib" ***!
\***********************/
/***/ ((module) => {
"use strict";
module.exports = require("zlib");;
/***/ })
Other things I tried were--ALL of which did not work:
I was hoping this would alias zlib and actually put in the browserify-zlib code.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
alias: { zlib: '/work/proj/node_modules/browserify-zlib/lib/index.js' },
fallback: {} } }
Same as the previous example but thought by disabling the fallback the alias/polyfill would go into the output. This is what others online had success with.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
alias: { zlib: '/work/proj/node_modules/browserify-zlib/lib/index.js' },
fallback: { zlib: false } } }
Here I just hoped to not include zlib to see if Webpack would omit it with a node target.
resolve:
{ modules: [ '/work/proj/node_modules' ],
extensions: [ '.js', '.json' ],
fallback: { zlib: false } } }
Lastly I tried to use the plugin node-polyfill-webpack-plugin but with the node target it does not seem to do anything. If I chose a web target the plugin seems to work as I'd expect (taken from here). Again, I'd prefer a node target so it uses native modules and the setup seems cleaner; but maybe this is the only approach. If this is the approach then how to support fs and other non-browser modules that IoT.js supports natively?
...
plugins = [ new NodePolyfillPlugin({ excludeAliases: [] }) ];
It seems that when the node target is selected there is no way to override any of the default/boilerplate code added to the output file. Does anyone have experience with IoT.js and Webpack, or overriding the default Webpack 5 code for node and use a polyfill instead? Not sure if a Webpack plugin is an approach. I am a little new to Webpack. Could this be a problem with Webpack? Any help would be appreciated.

Rollup with d3 namedExports issue

I have a package I am making which uses d3. Of course, in my rollup.config.js file I declare d3 as both an external and global package:
let config = {
...,
output: {
external: ['d3'],
globals: {d3:'d3'},
...,
},
...
}
and I might have a function in a file somewhere like:
import * as d3 from 'd3'
...
export function someFunc(arg1, arg2) {
d3.select(arg1)
d3.min(arg2)
}
...
and when I bundle my code with rollup -c I get the nice warning that
src/modules/some-file.js
selection is not exported by node_modules/d3/dist/d3.node.js
so I go back to my rollup.config.js file and add the following:
// inside config
plugins: [
...,
commonjs({
...,
namedExports: {
'node_modules/d3/dist/d3.node.js': [
'selection', 'min',
]
},
...,
})
...,
]
and now my bundle has no warnings or complaints... but when I go to use my bundled code, I get errors like:
TypeError: d3_node_1 is null
TypeError: d3_node_2 is null
where d3_node_1 appears where I have d3.select in my code and d3_node_2 appears where d3.min is.
How do I get around this?
You probably have to use the rollup-plugin-node-resolve to use third party modules from node_modules
adding 'module' to mainFields seemed to solve it
nodeResolve({
mainFields: [
'module',
'main', 'jsnext'
]
}),

Enable inline javascript in LESS

I would like to use inline js in my less files but I get the following message:
Inline JavaScript is not enabled. Is it set in your options?
How can I enable that?
I had same problem, I use webpack with less loader, I needed to add javascript option in less loader config:
{
test: /\.less$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "less-loader",
options: {
javascriptEnabled: true
}
}]
}
I found in the sourcecode of less compiler: https://github.com/less/less.js/blob/3.x/bin/lessc
that they parse js less option in this way:
case 'js':
options.javascriptEnabled = true;
break;
case 'no-js':
console.error('The "--no-js" argument is deprecated, as inline JavaScript ' +
'is disabled by default. Use "--js" to enable inline JavaScript (not recommended).');
break;
So you should probably use '--js' in a static compilation ( command line ) or 'javascriptEnabled: true' in a dynamic compilation ( like webpack loader ) to enable javascript.
Just to update the accepted answer,
From 3.11.1, if you use just options, it will throw :
ValidationError: Invalid options object. Less Loader has been
initialized using an options object that does not match the API
schema.
- options has an unknown property 'javascriptEnabled'. These properties are valid: object { lessOptions?, prependData?,
appendData?, sourceMap?, implementation? }
In less#3.11.1, it's not just options that should be used, but lessOptions like this :
{
test: /\.less$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true
}
}
}]
}
Updated: May 2020
For less-loader version 6.1.0^.
In "less-loader" version 6.1.0^ they made breaking changes to the loader that, if you used something like Ant Design (or other LESS and JS loaders) you would nomally add the javascriptEnabled: true flag to the "options" object in your Webpack configuration.
In version 6.1.0^ this was change to be placed in the lessOptions object under the options configuration for the less loader. Here is the solution I used, that works for my Webpack configuration bundle.
module: { rules: [{
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
}
}
}
]
}]}
Notice that the javascriptEnabled flag is not under the top-level options object, but it, instead, under the lessOptions sub-object. That is the latest updated standard as of less-loader version 6.1.0^.
If you're using the lessc the cli interface then you just need --js on the end.
lessc --js ./styles/theme.less ./styles/theme.css
I got this problem when using the newest version of less. Then I switched to version 2.7 and I had it fixed.
Inline JavaScript was disabled by default for security concerns. What was happening is that online generators would sometimes allow configuration of Less variables that they then interpreted directly.
This was vulnerable to code injection, meaning that JavaScript could be injected into a Less style sheet that ran on a server directly.
For this reason, inline JavaScript has been deprecated (set to false by default in 3.x), and the replacement for that is the #plugin syntax and using a proper JS plugin. - (See: http://lesscss.org/features/#plugin-atrules-feature)
Yes, you can still set compilation options to javascriptEnabled: true, but this is not considered best practice for style sheets. In general, your style sheet should not have JS in it. It's better to use a plugin.
Yes to everything that #matthew-dean and #davide-carpini said... but for anyone looking for the Grunt-LESS code snippet here you go:
less: {
dev: {
options: {
paths: ['Content/less'],
plugins: [
new(require('less-plugin-autoprefix'))({
browsers: ['last 2 versions']
}),
new(require('less-plugin-clean-css'))({
sourceMap: true,
advanced: true
})
],
relativeUrls: true,
javascriptEnabled: true
},
files: {
'Content/less/site.css': 'Content/less/site.less'
}
}
},
this is working for my implementation using "grunt-contrib-less": "^2.0.0" ... your mileage may vary
I had the same problem but in vue-cli 4 + iVueUi theme customization. Maybe somebody has same troubles like me. And that's solution:
Create or use existing vue.config.js file at the root of your project. And add this code (or partically add) into it.
module.exports = {
css: {
loaderOptions: {
less: {
javascriptEnabled: true
}
}
}
};
But remember that js is disabled by default for security reasons. So that's at your own risk.
as i was using craco and craco-less and also customizing ant design's variable through my .less file ,modifying craco.config.js like below fixed my problem:
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
javascriptEnabled: true,
},
},
},
},
],
}
For any Vite 3 users out there, the following is how I enabled inline javascript for a project with less/react.
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
},
});
However, inline javascript is a deprecated feature on less (link)
False by default starting in v3.0.0. Enables evaluation of JavaScript inline in .less files. This created a security problem for some developers who didn't expect user input for style sheets to have executable code.

Categories