SVG sprite issue with Laravel Mix - svg

I'm struggled with the following and would appreciate any help...!
I try to use Laravel Mix (v5.0.4) and extend it with SVG sprite loader (svg-sprite-loader) to generate SVG sprite. I have the following folder structure:
resources/
images/
image.jpg
sass/
app.scss
svg/
file1.svg
file2.svg
webpack.sprite.js
webpack.mix.js
The content of webpack.mix.js:
const mix = require('laravel-mix');
require('./webpack.sprite');
const toCss = 'public/css';
mix.sass('resources/sass/app.scss', toCss)
.options({
sassOptions: {
outputStyle: 'nested',
}
})
.sprite();
The content of webpack.sprite.js:
const mix = require('laravel-mix');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const path = require('path');
class Sprite {
dependencies() {
return ['svg-sprite-loader'];
}
webpackPlugins() {
return new SpriteLoaderPlugin({plainSprite: true});
}
webpackRules() {
return {
test: /\.svg$/,
include: path.resolve(__dirname, 'resources', 'svg'),
use: [
{
loader: 'svg-sprite-loader',
options: {
extract: true,
spriteFilename: path.resolve(__dirname, 'resources', 'images') + 'sprite.svg',
runtimeCompat: true
}
},
'svg-transform-loader',
'svgo-loader'
]
};
}
}
mix.extend('sprite', new Sprite());
It does NOTHING in regards sprite, but it generates the CSS from SASS! :( I don't know why... Tried to "debug" it with some console.log() in the extension and it was hit, I saw the log messages in the console. But the sprite wasn't generated.
I also tried to use just hardcoded, relative paths in the extension without path. Didn't help.
Any idea?
Thanks in advance!

I have a feeling this is related to Webpack5's new Asset module.
https://webpack.js.org/guides/asset-modules/
For assets to be written to disk, or possibly primed to be handed off to large plugins you need to now specify asset type and generator to best define a filename for these assets.
webpackRules() {
return {
test: /\.svg$/,
include: path.resolve(__dirname, 'resources', 'svg'),
type: 'asset/resource',
generator: {
'filename': '[name][ext]'
},
use: [
{
loader: 'svg-sprite-loader',
options: {
extract: true,
spriteFilename: path.resolve(__dirname, 'resources', 'images') + 'sprite.svg',
runtimeCompat: true
}
},
'svg-transform-loader',
'svgo-loader'
]
};
}
If still no luck try an alternative plugin:
https://www.npmjs.com/package/webpack-svg-spritely

Related

Webpack generates js files with css / scss files

Description
In webpack I am using mini-css-extract-plugin:
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[name].[hash].css',
})
]
To load scss files in chunk files:
{
test: /\.scss$/,
use: [
{ loader: MiniCssExtractPlugin.loader, options: {
hmr: isdev,
reloadAll: true
}
},
"css-loader",
"sass-loader",
]
}
When I load a scss with an dynamic import:
import(/* webpackChunkName: "test" */ 'test.scss')
It will generate a test.[hash].css containing the styles and a test.[hash].js:
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[15],{
/***/ 81:
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ })
}]);
Problem
I want to minimize the delay and loaded files so I find it redundant to have a nearly empty test.[hash].js file.
Do you have a way to either include the scss in the js file (see Idea 1) or to not emit/use the nearly empty js file?
Idea 1: not using mini-css-extract-plugin
My first idea was not using mini-css-extract-plugin for dynamic imported scss, but this will include a lot css-base stuff in the js (https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/255).
Here is an extract of code that could interrest you. It's coded in live here, so there is maybe some error I don't know.
I use an alternative way but nearby inside my own project.
The behaviour is :
Use the Event Hook Plugin and call it when the webpack is done
Loop through each file
If file is css and have the same name as with js extension
Then remove the js file
const EventHooksPlugin = require('event-hooks-webpack-plugin');
const path = require('path');
const fs = require('fs');
const _ = require('underscore');
plugins: [
new EventHooksPlugin({
done: () => {
const publicDir = __dirname + '/public';
const files = fs.readdirSync(publicDir);
_.each(files, file => {
if (path.extname(file) !== '.css') { return ;}
const fileJs = file.replace('.css', '.js');
if (!fs.existsSync(fileJs)) {return;}
fs.unlinkSync(fileJs);
});
}
})
]

Webpack Usage of chunkFilename

Iam having trouble understand the use of chunkFilename property. Where do we need it?
I cant find any usage in webpack docs.
const path = require('path');
const config = {
entry:['./util.js','./index.js'],
output: {
path: path.resolve(__dirname, 'public'),
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.js'
},
module: {
rules: [
{ test: /\.js$/, exclude:/node_modules/ }
]
}
};
module.exports = config;
chunkFilename is used for naming chunks :]
Chunks are created using dynamic import such as import('./file') or webpacks require.ensure() syntax.

How to include a few node_modules package in babel-node

I'm trying to include #mycompany/package1, and #mycompany/package2 to be compiled along with the rest of my code using babel-node. Since package1 and package2 are in ES6. (Also note I'm not using Webpack)
In my jest config I added the below option into my jest config which works fine. When testing the code will compile the packages correctly
"transformIgnorePatterns": [
"/node_modules/(?!(#mycompany)/).*/"
],
But when trying to run babel-node I get errors.
In my babel.config.js
module.exports = {
presets: [
'#babel/preset-flow',
[
'#babel/preset-env',
{
targets: {
node: 8
}
}
]
],
plugins: ['#babel/plugin-proposal-class-properties']
};
I tried adding the below code to my babel.config.js but it still complains about ES6 errors within my node_modules/#mycompany/package1
I tried to include the viz package but then babel wouldn't compile my src files
include: [path.resolve(__dirname, 'node_modules/#mycompany/package1')]
include: ['/node_modules/((#mycompany)/).*/']
I tried to exclude everything but #mycompany packages but I still get transpile errors in my package1
exclude: [/node_modules\/(?!(#mycompany)\/).*/],
I tried playing with ignore but those don't seem like they are the right options based on reading the docs
I found out that we can do this with webpack to help bundle the packages with the rest of your code.
This is my webpack file for NodeJS.
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
const spawn = require('child_process').spawn;
const nodeEnv = process.env.NODE_ENV;
const isProduction = nodeEnv === 'production';
const compiler = webpack({
entry: ['#babel/polyfill', './src/server.js'],
output: {
path: path.resolve(__dirname, 'lib'),
filename: 'server.bundle.js',
libraryTarget: 'commonjs2'
},
externals: [
nodeExternals({
whitelist: [/#mycompany\/.*/]
})
],
plugins: plugins,
target: 'node',
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!(#mycompany)\/).*/,
use: {
loader: 'babel-loader',
options: {
configFile: './babel.config.js'
}
}
}
]
}
});
if (isProduction) {
compiler.run((err, stats) => {
if (err) {
console.error(err);
return;
}
console.log(
stats.toString({
colors: true
})
);
});
} else {
let serverControl;
compiler.watch(
{
aggregateTimeout: 300,
poll: 1000
},
(err, stats) => {
if (serverControl) {
serverControl.kill();
}
if (err) {
console.error(err);
return;
}
console.log(
stats.toString({
colors: true
})
);
// change app.js to the relative path to the bundle created by webpack, if necessary
serverControl = spawn('node', [
path.resolve(__dirname, 'lib/server.bundle.js')
]);
serverControl.stdout.on('data', data => console.log(data.toString()));
serverControl.stderr.on('data', data => console.error(data.toString()));
}
);
}
Note the most important part is
Adding webpack-node-externals. Since this is a node.js server we don't need to bundle the node_modules.
Make sure you whitelist your package that you need to be compiled/bundled and also make sure you have your packages included to be compiled in your babel-loader
nodeExternal tells webpack know not to bundle ANY node_modules.
whitelist is saying that we should bundle the packages we listed
externals: [
nodeExternals({
whitelist: [/#mycompany\/.*/]
})
]
This line means to exclude all node_modules EXCEPT #mycompany/* packages
exclude: /node_modules\/(?!(#mycompany)\/).*/,

NextJS with react-svg-loader fails

I'm having trouble getting my configuration right for this. I have a NextJS setup with next-css and I'm trying to add react-svg-loader to the configuration:
next.config.js:
const withCSS = require("#zeit/next-css");
module.exports = withCSS({
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
localIdentName: "[local]__[hash:base64:4]"
},
webpack(config, options) {
const { dev, isServer } = options;
config.module.rules.push({
test: /\.svg$/,
use: [
{
loader: "react-svg-loader",
options: {
jsx: true // true outputs JSX tags
}
}
]
});
return config;
}
});
The svgs will still fail to load:
{ Error: (client) ./svgs/pencil.svg 10:9 Module parse failed:
Unexpected token (10:9) You may need an appropriate loader to handle
this file type.
Looks like my config above doesn't work but I can't quite figure out why.

got "Element type is invalid: expected a string" when trying to server side rendering react

I get this error:
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
When trying to use ReactDOMServer.renderToStaticMarkup.
This is my react app:
"use strict";
import React from 'react'
module.exports = () => {
return (
<div></div>
);
};
And this is my node server rendering code:
"use strict";
const path = require('path');
const webpack = require('webpack');
const React = require('react'), ReactDOMServer = require('react-dom/server'),
DOM = React.DOM, body = DOM.body, div = DOM.div, script = DOM.script;
webpack({
target: "node",
entry: [
path.resolve(__dirname, '../js', 'app.js'),
],
module: {
loaders: [
{
exclude: /node_modules/,
loader: 'babel',
test: /\.js$/,
},
]
},
output: {filename: 'app.bundle.js', path: __dirname},
},() => {
const App = React.createFactory(require('./app.bundle.js'));
let html = ReactDOMServer.renderToStaticMarkup(body(null,
div({
id: 'root', dangerouslySetInnerHTML: {
__html: ReactDOMServer.renderToString(App())
}
})
));
});
Does anyone have an idea what cause this error and how to fix this?
Thanks in advance.
It appears that the bundle is not compatible by default with commonjs. As I read in webpack docs, you need to add libraryTarget:'commonjs2' to your output object in order to do it.
Like this:
output: {filename: 'app.bundle.js', path: __dirname,libraryTarget:'commonjs2'}

Resources