Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't work on the server side #19

Closed
oliverox opened this issue Dec 31, 2015 · 26 comments
Closed

Doesn't work on the server side #19

oliverox opened this issue Dec 31, 2015 · 26 comments

Comments

@oliverox
Copy link

Thanks for the great work.
I'm trying to get this loader to work on server side as well. Since it's touches document directly, I guess getting it to work on the server is currently not possible. Can you please confirm?
If that is the case, do you have any plans of making it to work on the server as well?

@kisenka
Copy link
Contributor

kisenka commented Dec 31, 2015

@oliverox you can implement your own sprite behaviour via spriteModule option. Something like this:

webpack.config.js

...
  module: {
    loaders: [
      {
        test: /\.svg/,
        // define path to custom sprite module
        loader: 'svg-sprite?spriteModule=' + path.resolve(__dirname, 'server-side-rendering-sprite-module.js')
      }
    ]
  }

server-side-rendering-sprite-module.js

// simple sprite implementation which renders into string
function Sprite() {
  this.symbols = [];
}

Sprite.prototype.add = function(image) {
  this.symbols.push(image);
};

Sprite.styles = ['position:absolute', 'width:0', 'height:0', 'visibility:hidden'];

Sprite.prototype.render = function() {
  return [
    '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="'+ Sprite.styles.join(';') +'">',
      '<defs>',
        this.symbols.join(''),
      '</defs>',
    '</svg>'
  ].join('');
};

module.exports = new Sprite();

my-app.js

require('./image.svg');

var sprite = require('./server-side-rendering-sprite-module');
var renderedSprite = sprite.render();
console.log(renderedSprite);

@kisenka
Copy link
Contributor

kisenka commented Jan 9, 2016

@oliverox does it helped?

@oliverox
Copy link
Author

@kisenka Thanks for the help in the right direction. I have not had enough time to implement it yet as I've been moving over the new year. Will try it soon. Thanks again.

@olegakbarov
Copy link

@kisenka I stumbled upon same problem. Could you please elaborate on process of implementing the server-sider rendering module for React app?

My node app generates sprite and inlines it into html (this works well):

app.use((req, res, next) => {
  const html = renderToString(
    <Root />
  );

  let renderedSprite = Root.sprite;

  res.send(template({ html, env, renderedSprite }));
});

and my components looks like this

import svg_aws from './img/aws.svg';

class Icon extends React.Component {
  render() {
    let { glyph } = this.props;
    return (
      <svg
        className="icon"
        width="81px"
        height="81px"
        dangerouslySetInnerHTML={{__html: '<use xlink:href="' + glyph + '"></use>'}}
      />
    )
  }
}

<Icon glyph={svg_react} />

The problem is that when webpack compiles it (with target: node) the svg_aws is undefined. So to my understanding the problem boils down to getting the sprite xlink:href at compile time. Any thoughts on this?

@olegakbarov
Copy link

Well.. i came up with crappy solution actually:

const sprite = require('../../utils/svg-server-rendering.js');

...

let ids = sprite.symbols.map(i => {
   return i.match(/id="(.*?)"/g)
});

let arr = ids.map(j => j[0].replace('id=', '#').replace(/\"/g, ""))

arr.map(el => <Icon glyph={el} />)

Pretty sure that it is not only ugly, but also wrong somewhere 😐

UPD:

aaaand it does not work on webpack dev server

@kisenka
Copy link
Contributor

kisenka commented Mar 3, 2016

import svg_aws from './img/aws.svg';
<Icon glyph={svg_react} />
svg_aws
svg_react

Maybe wrong var reference?

@olegakbarov
Copy link

No, i map all of them and they all undefined when render on server. Also initial version based on docs worked perfectly on development server (without SSR).

@kisenka
Copy link
Contributor

kisenka commented Mar 4, 2016

@olegakbarov sorry, I am vacation till 21 March so I can't help you now

@olegakbarov
Copy link

No problem, i'll write up if got some results on this.

@qur2
Copy link

qur2 commented Mar 16, 2016

Also struggling with server-side rendering. Initially, the error I got was a type error because React is undefined. Found out that it seems it's the babel-loader trying to do its job (there is no webpack when running with node -r babel-register). I then tried to just get the correct id generated by node, assuming the sprite would already be setup thanks to the webpack build, but I don't get any svg file in my build output anyway and I'm now very confused.

@qur2
Copy link

qur2 commented Mar 16, 2016

I believe for SSR, we need a node require hook that will generate the correct ID for a given path, then it should all work fine since the sprite will be loaded client-side.
I would give it a go, but I'm first trying to get a sprite generated at all.

@kisenka kisenka reopened this Apr 14, 2016
@kisenka
Copy link
Contributor

kisenka commented Apr 14, 2016

Sorry for delay. I am working on it

@danilobuerger
Copy link

@kisenka do you still intend to work on this?

@kisenka
Copy link
Contributor

kisenka commented Apr 25, 2016

I've created the testcase https://github.com/kisenka/svg-sprite-loader-ssr-testcase for server-side rendering.

  1. Fork it.
  2. npm install
  3. npm run sprite-test

You will see that output from sprite loader which contains the whole sprite.
You can add your testcases in you forks and tell me.

@danilobuerger
Copy link

Shouldn't something like SSR live in the default sprite impl? The very least it should do on a node environment it just return the symbol id (without using document) instead of failing.

@kisenka
Copy link
Contributor

kisenka commented Apr 26, 2016

@danilobuerger you're right. Sprite loader firstly created for in browser usage, so it works with browser environment out of the box. I think it will be good if sprite loader will use environment specific sprite implementation which depends on target webpack config option. Thoughts?

@danilobuerger
Copy link

Hmm, so what we need to consider is client-only and server+client (render server side, pick up on client side) and server-only setups. Probably need to also consider outputing to a svg file (see #23).

Overall it might make more sense splitting this up in multiple loaders. One that returns symbol ids (or external ref + symbol ids) and builds the svg map. Another loader to include that svg map either as file or load it in dom or output it in some other way.

client-only

If inline, return symbol id on require. Include svg map in dom, updateable (?).
If file, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin.

server-only

If inline, return symbol id on require. Output svg map so it can be rendered.
If file, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin.

client+server

If inline, return symbol id on require. Output svg map so it can be rendered. Client should not include svg map in dom as its already present. Maybe it can updated it instead like react-helmet does for example.
If file, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin. Client should not include svg map in dom.

@qur2
Copy link

qur2 commented Apr 27, 2016

Just as a heads-up, for my project, I switched to making a webpack bundle for the server as well (as per this article: http://jlongster.com/Backend-Apps-with-Webpack--Part-I) to avoid needing duplicate logic for every loader that node should know about.

@hyatt03
Copy link

hyatt03 commented Jun 16, 2016

Hi everyone! I've run into a similar issue, however I didn't want to webpack my entire backend so I came up with a kinda hacky solution.

I was already using ignore-styles as I import relevant stylesheets into my react components, so in order to generate the correct server side markup I just needed to add the following to my bootstrapping file:

const path = require('path');
const crypto = require('crypto');

require('ignore-styles').default(['.scss', '.css', '.svg'], (module, filename) => {
  const base = path.basename(filename);
  if (base.indexOf('.svg') > 0) {
    module.exports = '#' + crypto.createHash('md5').update(filename).digest("hex");
  }
});

Note this works with the following webpack config:

{
  ...
  module: {
    loaders: [
      {
        test: /\.svg$/,
        loader: 'svg-sprite?' + JSON.stringify({
          name: '[pathhash]',
          prefixize: true
        })
      }
    ]
  },
  ...
}

I hope this helps somebody! 😃

@wmertens
Copy link
Contributor

wmertens commented Jul 19, 2016

I can confirm that the approach provided by @kisenka in #19 (comment) works for me.

I had to make the webpack config for the ?spriteModule=server-side-rendering-sprite-module conditional on the prerender build, and then in my app I just needed to import the server-side-rendering-sprite-module module and put the result of sprite.render() in the outgoing HTML.

However, I think that the approach outlined in #45 will be more efficient and easier to set up.

@elado
Copy link

elado commented Aug 24, 2016

Confirming @hyatt03's solution works well! Thanks for sharing.

@lrojas94
Copy link

lrojas94 commented Apr 4, 2017

Hello!!

Are there any updates on this? (as I see this to be a feature for 2.0).
Also, could you, given you have the chance, point me in the right direction to use with React? Though I did check the example, I can't really see how to implement it with React.

Thanks in advance!

@kisenka
Copy link
Contributor

kisenka commented Apr 4, 2017

@lrojas94 work in progress, I planning to release alpha version on the next week.

Also, could you, given you have the chance, point me in the right direction to use with React?

Do you have concrete question about loader with React?

@lrojas94
Copy link

lrojas94 commented Apr 4, 2017

Thing is I'm using a server side rendered Application, which is why this loader is failing when going through the app initialization. In the meantime, I managed to make it work by only requiring the plugin if typeof window !== 'undefined', it's a temporal fix, but works, so will be looking forward to the alpha version next week :)

Thanks for taking the time to look out my problem here 👍

@kisenka kisenka mentioned this issue Apr 23, 2017
@kisenka
Copy link
Contributor

kisenka commented Apr 23, 2017

@oliverox
@olegakbarov
@qur2
@danilobuerger
@hyatt03
@wmertens
@elado
@lrojas94

Hey guys svg-sprite-loader@2.0 with server side rendering is on the way, please read about it here #91 and take part in discussion/voting. Thanks!

@kisenka
Copy link
Contributor

kisenka commented Apr 28, 2017

Check 2.0.1 release

@kisenka kisenka closed this as completed Apr 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants