2

I'm trying to return an SVG file located in the same folder as the Javascript file when a function is executed. The function is called returnSVG, the SVG is called image.

import image from './image.svg'

returnSVG= () => {
 return image;
}

But when I call it I get this: /static/media/image.f85cba53.svg

Raphael Deiana
  • 750
  • 4
  • 20
Juan CA
  • 156
  • 4
  • 15
  • 1
    That's because the "./" indicates that there's more to the path, it's relative to the enclosing folder. – iPhoenix Dec 13 '19 at 17:19
  • Thanks! Now I get another error: `Module not found: Can't resolve 'mergedStatusIcon.svg' in `'(the route of the file)'` – Juan CA Dec 13 '19 at 17:27
  • Is this in react or in some other context? In what sense are you trying to import it and return it? – TKoL Dec 13 '19 at 17:40
  • You should take a look at this post : https://stackoverflow.com/questions/42575712/webpack-sass-cannot-resolve-images – Raphael Deiana Dec 13 '19 at 17:42
  • The purpose of the function is to return a specific SVG that later should be displayed in the HTML. In that same JS file, I call later the function with `{this.returnSVG()}`. – Juan CA Dec 13 '19 at 18:00

1 Answers1

3

An SVG file contains a textual representation of a graphic, encoded in an XML-like syntax.

So, you cannot simply import thing from 'filepath' because the file contents are not a JavaScript module, a loadable binary extension, or a JSON-encoded string.

Rather than importing what cannot be imported, the correct approach would be to explicitly read the contents from the file:

const { readFileSync } = require('fs')
const returnSvg = (path = './image.svg') => return readFileSync(path)

Note that this example uses the synchronous version of the fs function to read a file. This will pause (block) all processing in NodeJS until the contents have been read.

The better solution would be to use Promises via async/await as in:

const { readFile } = require('fs')
const { promisify } = require('util')
const asyncReadFile = promisify(readFile)

const returnSvg = async (path = './image.svg') => {
  const data = await asyncReadFile(path)

  // since fs.readFile returns a buffer, we should probably convert it to a string.
  return data.toString() 
}

Using Promises and async/await requires that all of your processing occur within the chain of promises, so you'd need to restructure your code to:

returnSvg()
  .then(data => { /* do something with the file contents here. */ })
  .catch(err => console.error(`failed to read svg file: ${err}`))

Remember, in NodeJS, operations can be either asynchronous (via callbacks or Promises which async/await uses "under the covers"), or synchronous (via special "blocking" calls).

One option, if you choose to use the synchronous version would be to load all your files into your server before you call app.listen(8080, () => console.log('listing on 8080') but this implies you know what files will be required before you run your server and would not work for dynamically loaded files.

You might imagine doing something like:

const data = readSvg()
console.log(data)

or perhaps:

let data
readSvg().then(res => data = res)
console.log(data)

but neither will work because an asynchronous function (one defined with the async keyword) can only return a Promise.

Both attempts will not print any usable value to the console because at the time console.log() is called, NodeJS has no idea what the value of data will be.

The rule of thumb here is:

  • You cannot return a value or interact with any higher context from within a Promise.
  • Any manipulation of the data generated within a Promise chain, can ONLY be accessed from within its own chain.
Rob Raisch
  • 17,040
  • 4
  • 48
  • 58