airbnb / react-sketchapp

render React components to Sketch ⚛️💎
http://airbnb.io/react-sketchapp/
MIT License
14.95k stars 824 forks source link

import svg to render #530

Closed SilentFlute closed 2 years ago

SilentFlute commented 2 years ago

im a react developer, i want use this lib to render svg into sketch file, and i got some problems, i found this issue: #265 and join it to discuss, but im stuck as well, i got a suggestion from @macintoshhelper , so i open a new issue, if u see this issue at first time, i suggest u should view the issue that i referenced before, if the issue still there, then check this issue;)

------------------------------------------------------------------------------------------------- Reporting a bug or issue

Expected behavior: import a svg and rendered it successfully

Observed behavior: i got a error in sketch with redbox

How to reproduce:

  1. yarn
  2. yarn dist
  3. open sketch create a new doc and use this plugin

os version: 10.15.7

Sketch version: 75 (129697)

node version: 14.17.6

Please attach screenshots, a zip file of your project, and/or a link to your github project https://github.com/SilentFlute/react2sketch-svg.git

@macintoshhelper hi, it's me again, i modified the project, but i know, i cant understand ur advices all, may be at all?

i replace react-native-svg with react-sketchapp, and modified the file as below: webpack.skpm.config.js:

const path = require('path');

module.exports = (config) => {
  if (process.env.LOCAL_DEV) {
    config.module.rules[1].test = /^(?!.*\.(jsx?|tsx?|json|svg|md|nib|xib|framework|xcodeproj|xcworkspace|xcworkspacedata|pbxproj)$).*/;
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: ['@skpm/babel-preset'],
          },
        },
        {
          loader: '@svgr/webpack',
          options: {
            native: true,
            svgoConfig: {
              plugins: [{ convertPathData: false }],
            },
            babel: false,
          },
        },
      ],
    });  
    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve.alias,
        'react-sketchapp': path.resolve(__dirname, './Svg.js'),
      },
    };
  }
};

and then, add a file in the project, of course at the root directory, and just like u mentioned before: Svg.js

import React from 'react';
import { Svg } from 'react-sketchapp';

const {
  Circle, ClipPath, Defs, Ellipse, G, Image, Line, LinearGradient,
  Path, Pattern, Polygon, Polyline, RadialGradient, Rect, Stop,
  Symbol, Text, TextPath, TSpan, Use
} = Svg;

const Mask = () => <></>;
const _G = ({ mask, ...props }) => (mask ? <></> : <G {...props} />);

const _Use = ({ xlinkHref: href, ...props }) => <Use href={href} {...props} />;
const _Image = ({ xlinkHref: href, ...props }) => (
  <Image href={href} {...props} />
);

export {
  Mask, Circle, ClipPath, Defs, Ellipse, _G as G, _Image as Image,
  Line, LinearGradient, Path, Pattern, Polygon, Polyline, RadialGradient,
  Rect, Stop, Symbol, Text, TextPath, TSpan, _Use as Use
};

export default Svg;

what confused me is in the my-command.js, u mentioned dynamic import, local file, and webpack compile, i modified this file as below:

import * as React from 'react';
import { render, Artboard } from 'react-sketchapp';
const ImgSvg = require('./imgs/sketch.svg');

const Document = () => {

  return (
    <Artboard
      name="my-image"
      style={{
        width: 1920,
        height: 1080
      }}
    >
      <ImgSvg></ImgSvg>
    </Artboard>
  );
}

export default () => {
  render(<Document />, context.document.currentPage());
};

all right, it doesn't modified at all, cuz i cant understand ur description, so i still need ur help...

macintoshhelper commented 2 years ago

You are aliasing react-sketchapp, which will prevent anything from rendering at all. What is happening, is that the @svgr/webpack Webpack loader is generating a file, which imports react-native-svg. You need to replace the react-native-svg with the Svg.js file, via Webpack import aliasing, which you can think of as a find/replace. Please read the Webpack docs on this. Ignore dynamic importing, that’s a React web concept which isn’t supported in a Node.js/JavaScriptCore environment, just do a normal import.

Svg.js

const path = require('path');

module.exports = (config) => {
  if (process.env.LOCAL_DEV) {
    config.module.rules[1].test = /^(?!.*\.(jsx?|tsx?|json|svg|md|nib|xib|framework|xcodeproj|xcworkspace|xcworkspacedata|pbxproj)$).*/;
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: ['@skpm/babel-preset'],
          },
        },
        {
          loader: '@svgr/webpack',
          options: {
            native: true,
            svgoConfig: {
              plugins: [{ convertPathData: false }],
            },
            babel: false,
          },
        },
      ],
    });  
    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve.alias,
-        'react-sketchapp': path.resolve(__dirname, './Svg.js'),
+        'react-native-svg': path.resolve(__dirname, './Svg.js'),
      },
    };
  }
};

my-command.js

import * as React from 'react';
import { render, Artboard } from 'react-sketchapp';
- const ImgSvg = require('./imgs/sketch.svg');
+ import ImgSvg from './imgs/sketch.svg';

const Document = () => {

  return (
    <Artboard
      name="my-image"
      style={{
        width: 1920,
        height: 1080
      }}
    >
      <ImgSvg></ImgSvg>
    </Artboard>
  );
}

export default () => {
  render(<Document />, context.document.currentPage());
};
SilentFlute commented 2 years ago

right, alias, im not so familiar with webpack, just use scaffold instead, now i understand: the webpack loader @svgr/webpack use react-native-svg, and i need use Svg.js to instead react-native-svg, to let the svg image rendered successfully, and also the dynamic import

accordingly, i modified those 2 files as u said: my-command.js and webpack.skpm.config.js, then open sketch to use the plugin, but i still got a error:

Error: Could not find renderer for type 'xxx.svg'

missingRendererError

map

__skpm_run
__skpm_run

and that confused me a lot, i used the Svg.js to instead the react-native-svg for render the svg image, but i still got a error, but how? (sigh)

macintoshhelper commented 2 years ago

Ah, the loader is never getting initialised. You need to remove if (process.env.LOCAL_DEV) {

const path = require('path');

module.exports = (config) => {
-  if (process.env.LOCAL_DEV) {
    config.module.rules[1].test = /^(?!.*\.(jsx?|tsx?|json|svg|md|nib|xib|framework|xcodeproj|xcworkspace|xcworkspacedata|pbxproj)$).*/;
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: ['@skpm/babel-preset'],
          },
        },
        {
          loader: '@svgr/webpack',
          options: {
            native: true,
            svgoConfig: {
              plugins: [{ convertPathData: false }],
            },
            babel: false,
          },
        },
      ],
    });  
    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve.alias,
        'react-native-svg': path.resolve(__dirname, './Svg.js'),
      },
    };
-  }
};
SilentFlute commented 2 years ago

oh yeah, of course, process.env.LOCAL_DEV means in the development mode, but when i build my project/code, it's production mode, right right, when i modified that, it worked, finally, it worked, thanks again! and one more question: just as u mentioned in another issue that i referenced in the beginning, u said that i should use nodejs to save th svg into this project, and import this svg to render, that means:

  1. use nodejs fetch a svg and save it into my react2sketch project
  2. use shell script to write some codes into the my-command.js, like: import ImgSvg from '{the svg that i saved from 1st step}'; and <ImgSvg></ImgSvg>
  3. after 2nd step, build the project

is that right?

macintoshhelper commented 2 years ago

Uhh, I guess that would work, but it would be really hacky. I would just have the necessary SVGs locally and import them by hand. You might be able to make a codemod to inject imports into a file, or could create a react-sketchapp Webpack loader to fetch and compile the SVG, then you could do import Svg from 'http://localhost:8080/file.svg';

macintoshhelper commented 2 years ago

Some ideas here (including a Babel plugin): https://dev.to/emilios1995/dynamic-imports-in-react-native-9k5

SilentFlute commented 2 years ago

emmmm, in my situation, i will deal with all the svg images, not only some necessary svgs, and the svg might have many structures, cuz this project will play a middle layer role for the entire project, even if this entire project is still in my mind, haha, so about render svg is really hacky and the idea u posted, i check it out, it's a good idea, it's helpful, i will consider about it, thanks again