JetBrains / svg-sprite-loader

Webpack loader for creating SVG sprites.
MIT License
2.02k stars 272 forks source link

Extracting is not working in Safari #102

Closed Teun87 closed 7 years ago

Teun87 commented 7 years ago

Dear sir,

As you mention, this solution currently does not work in Safari. This is a no-go for me at the moment, since our clients make use of Apple devices. Is it possible to implement an option to set stacking to true or false? If you set it to false it will generate a real sprite with gutters. This way it will work in Safari and even in older browsers if you include svg4everybody.

Regards, Teun

kisenka commented 7 years ago

Try to use svgxuse polyfill.

Teun87 commented 7 years ago

That only solves IE-problems and not solves the Safari stacking problem as far as I know/tested.

kisenka commented 7 years ago

It works for me in desktop Safari 10.0.3: image

And in Safari mobile (iOS 10.3): image

kisenka commented 7 years ago

@Teun87 does it works for you?

Teun87 commented 7 years ago

I will check later on. At the moment I'm very busy and not have been able to check the setup anymore. Will keep you notified.

Teun87 commented 7 years ago

Nope does not work for me. The examples you show make use of an actual sprite with grid/coordinates instead of the setup which is chosen for the extract option in the plugin. It extract the SVG's and stacks them on top of each other instead of creating a grid with coordinates. Also, I'm having a hard time to include the SVG's I reference in my .twig-files (instead of .html as in the example).

For convenience I've included the most important code fragments. Maybe something is wrong in my example?

webpack.config.js:

var webpack = require('webpack');
var glob = require('glob');
var path = require('path');

// Extract CSS
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// Setup two instances of ExtractTextPlugin for individual tests.
var extractSCSS = new ExtractTextPlugin('[name].[chunkhash].css');
var extractCSS = new ExtractTextPlugin('[name].[chunkhash].css');

var HTMLExtractor = new ExtractTextPlugin('[name].twig');

// Lint CSS
var StyleLintPlugin = require('stylelint-webpack-plugin');

var CleanWebpackPlugin = require('clean-webpack-plugin');
var ManifestPlugin = require('webpack-manifest-plugin');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

// Plugin for creating svg sprites
////var SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin');
var SpriteLoaderPlugin = require('svg-sprite-loader/plugin');

// Flag to check the node environment
var inProduction = (process.env.NODE_ENV === 'production');

//var PurifyCSSPlugin = require('purifycss-webpack');
//var HtmlWebpackPlugin = require('html-webpack-plugin');
//var InlineChunkManifestHtmlWebpackPlugin = require('inline-chunk-manifest-html-webpack-plugin');

module.exports = {
    entry: {
        app: [
            __dirname + '/craft/templates/src/js/main.js',
            // Post CSS
            __dirname + '/craft/templates/src/css/main.post.css'
        ],
        // Sass
        extraSassTest: [__dirname + '/craft/templates/src/scss/sass-styling.scss'],
        // todo: why is svgxuse being exported to /template-images
        vendor: ['jquery', 'vue', 'svgxuse']
    },
    output: {
        path: path.resolve(__dirname + '/public/dist/'),
        // Chunkhash instead of hash, to prevent renaming all files if one changes
        filename: '[name].[chunkhash].js'
    },
    module: {
        rules: [

            // (Post)CSS
            {
                test: /\.css$/,
                use: extractCSS.extract({
                    fallback: 'style-loader',
                    use: [
                        {
                            loader: 'css-loader',
                            options: {
                                autoprefixer: false,
                                sourceMap: true,
                                importLoaders: 1
                            }
                        },
                        'postcss-loader',
                        //This loader will automatically fix whatever it can in your stylesheets before pre-processing them
                        // {
                        //     loader: "stylefmt-loader",
                        //     options: {
                        //         config: "stylelint.config.js"
                        //     }
                        // },
                    ],
                })
            },

            // SASS
            {
                test: /\.scss$/,
                use: extractSCSS.extract({
                    use : [
                        {
                            loader: 'css-loader',
                            // options: {url: false}
                        },
                        'sass-loader',
                        'resolve-url-loader',
                        'sass-loader?sourceMap'
                    ],
                    fallback: 'style-loader'
                })
            },

            // TWIG
            {
                test: /\.twig$/,
                loader: HTMLExtractor.extract({ use: 'html-loader' })
            },

            // JS
            {
                test: /\.js$/,
                exclude: __dirname + '/node_modules/',
                use: 'babel-loader'
            },

            // Vue
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },

            // FONTS: Base setup for webfonts, caution: does not copy svg-format
            {
                test: /\.eot|ttf|woff|woff2$/,
                loader: 'file-loader',
                options: {name: './fonts/[name].[ext]'}
            },

            // SVG
            // Best option for now is to manually create the sprite via icomoon.io and place it in the
            // icons-folder.
            {
                test: /\.svg$/,
                loader: 'svg-sprite-loader',
                include: path.resolve(__dirname + '/craft/templates/src/icons'),
                options: {
                    extract: true,
                    spriteModule: '/craft/templates/src/icons'
                }
            },

            // IMAGES
            {
                test: /\.png|jpg|gif$/,
                loaders: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: './template-images/[name].[ext]'
                        }
                    },
                    // Optimize images with RELATIVE path which are used in the CSS files (only if in production env)
                    {
                        loader: 'img-loader',
                        options: {
                            enabled: inProduction,
                        }
                    }
                ]
            }
        ]
    },
    plugins: [

        HTMLExtractor,

        new SpriteLoaderPlugin(),

        new BrowserSyncPlugin({
            // browse to http://localhost:3000/ during development,
            // ./public directory is being served
            notify: false,
            open: false,
            proxy: 'http://webpack-base.dev',
            files: [
                'craft/templates/**/*.twig',
                'public/assets/dist/**/*.js',
                'public/assets/dist/**/*.css'
            ]
        }),

        // Extract CSS and optionally minimize it.
        extractCSS,
        extractSCSS,
        new webpack.LoaderOptionsPlugin({
           minimize: inProduction
        }),

        // Lint CSS
        new StyleLintPlugin({
            files: 'craft/templates/**/*.?(s)?(a|c)ss',
            emitErrors: false,
            // not working: quiet: true
        }),

        // Optional Clean plugin
        new CleanWebpackPlugin(['public/dist'], {
            root: __dirname,
            verbose: true,
            dry: false
        }),

        // Plugin to return the new hashed file-names for JS/CSS
        new ManifestPlugin(),

        // Insert chuck hash files into the HTML
        // new HtmlWebpackPlugin({
        //     template: './index.html'
        // }),
        // // InlineChunkManifestHtmlWebpackPlugin defaults to:
        // // { filename: 'manifest.json', manifestVariable: 'webpackManifest', chunkManifestVariable: 'webpackChunkManifest', dropAsset: false }
        // new InlineChunkManifestHtmlWebpackPlugin(),

        // Optional CSS purifier
        // new PurifyCSSPlugin({
        //     // Give paths to parse for rules. These should be absolute!
        //     paths: glob.sync(path.join(__dirname, 'index.html')),
        //     minimize: inProduction
        // }),

        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            minChunks: Infinity
        }),

        // Make jQuery work with Webpack. Only include if jQuery is necessary
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery"
        })

    ],

    // Select full version of vue instead of the (default) runtime-only build
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    }

};

// If production minify JS
if(inProduction){
    module.exports.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    )
}

index.twig (svg files are not picked up by the plugin):

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="{{ craft.laravelMix.mix('app.css') }}">
    <link rel="stylesheet" href="{{ craft.laravelMix.mix('extraSassTest.css') }}">
    <title>Document</title>
</head>
<body>

<h1>Test</h1>

<div class="test" id="app">
    <template></template>
</div>

<h2 class="testThree">Test Thrasdddeee</h2>

<h4>Test 12132312</h4>

<div class="o-container">
    <div class="o-column">Test</div>
    <div class="o-column">Test</div>
</div>

<div class="c-tabs">
    test
</div>

{#<svg class="icon icon-instagram">#}
    {#<use xlink:href="/dist/template-images/icon-instagram"></use>#}
{#</svg>#}
<img src="user.svg" alt="">
<img src="coffee2.svg" alt="">
<span class="name"> icon-instagram</span>

<div class="columns">
    <div class="column">1</div>
    <div class="column">2</div>
    <div class="column">3</div>
    <div class="column">4</div>
    <div class="column">5</div>
</div>

<div class="testFour">testino</div>

<script src="{{ craft.laravelMix.mix('vendor.js') }}" ></script>
<script src="{{ craft.laravelMix.mix('app.js') }}" ></script>

</body>
</html>

The icons are located in ~/craft/templates/src/icons/*.svg

Import in .SCSS files is working fine.

Another question: Is it also possible to scan for a specific directory to generate the sprite. For example, a big graphic does not need to be included in the sprite. I would love to include the .svg-format in the 'IMAGES'-test, but for know Webpack is warning that svg-files have 2 rules.

Hope you can help me out!

Thank you and regards, Teun

kisenka commented 7 years ago

@Teun87 it will better you to create repo with your setup which demonstrates described behaviour

Teun87 commented 7 years ago

Does this work for you? https://tcs_teun@bitbucket.org/theconceptstore/webpack-boilerplate.git

kisenka commented 7 years ago

@Teun87 Permission denied

Teun87 commented 7 years ago

Ok, I can add your name for the repo. Do you have a BitBucket account?

kisenka commented 7 years ago

yep, qtuzov@gmail.com

Teun87 commented 7 years ago

https://github.com/Teun87/webpack-boilerplate

kisenka commented 7 years ago

As I understand properly you are trying to join server-side PHP template engine with client-side code compiled with webpack. If so, you have 2 options:

  1. Import svg from js, sprite will be compiled and injected in DOM automatically when page loads. You refer to images like <svg class="icon icon-instagram"><use xlink:href="#instagram"></use></svg>.
  2. Import svg from CSS/SCSS. Sprite will be emitted as separate file. It's more convenient, but have issues with Apple devices. Implementing real sprite with gutters should fix this issue, but now I have no time to do it.

Is it also possible to scan for a specific directory to generate the sprite. For example, a big graphic does not need to be included in the sprite. I would love to include the .svg-format in the 'IMAGES'-test, but for know Webpack is warning that svg-files have 2 rules.

Yes, it's webpack require context feature.

// somewhere in your application entry point
const files = require.context('./craft/templates/src/icons', false, /\.svg$/);
files.keys().forEach(files);

// it will import all svgs files from craft/templates/src/icons
Teun87 commented 7 years ago

Ok, thanks for your advice. Will look later on if this creates a workable situation.

kisenka commented 7 years ago

Closing this, any related issues should be discussed here -> https://github.com/kisenka/svg-baker/issues/4