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

Ability to specify the name of the generated component #325

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## 3.1.0

You can specify the name of the component.

```js
{
test: /\.svg$/,
use: [{
loader: 'react-svg-loader',
options: {
componentName: (resource: string) => {
return config.environment.production === true
? null
: upperFirst(camelCase(path.parse(resource).name))
},
}
}]
}
```

## 3.0.3

- Ignore babel config files during transformation [#264](https://github.com/boopathi/react-svg-loader/pull/264)
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-react-svg/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "babel-plugin-react-svg",
"version": "3.0.3",
"version": "3.1.0",
"description": "Babel plugin to transform svg to react component",
"keywords": [
"babel",
Expand Down
68 changes: 42 additions & 26 deletions packages/babel-plugin-react-svg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import cssToObj from "./css-to-obj";
import { hyphenToCamel, namespaceToCamel } from "./camelize";
import BabelCore from "@babel/core";

export default function(babel: BabelCore) {
export default function (babel: BabelCore) {
const t = babel.types;
const restElement = t.restElement ? t.restElement : t.restProperty;

Expand Down Expand Up @@ -70,7 +70,7 @@ export default function(babel: BabelCore) {
// <tag style={{textAlign: 'center', width: '50px'}}>
if (name.node.name === "style") {
let csso = cssToObj(value.node.value);
let properties = Object.keys(csso).map(prop =>
let properties = Object.keys(csso).map((prop) =>
t.objectProperty(
t.identifier(hyphenToCamel(prop)),
t.stringLiteral(csso[prop])
Expand All @@ -89,29 +89,37 @@ export default function(babel: BabelCore) {
name.replaceWith(t.jSXIdentifier(hyphenToCamel(path.node.name.name)));
}
}
}
},
};

const exportBody = [
t.objectPattern([
t.objectProperty(
t.identifier("styles"),
t.assignmentPattern(t.identifier("styles"), t.objectExpression([])),
false,
true
),
restElement(t.identifier("props")),
]),
];

// returns
// export default (props) => ${input_svg_node}
const getExport = function(svg) {
const getExport = function (svg) {
return t.exportDefaultDeclaration(
t.arrowFunctionExpression(
[
t.objectPattern([
t.objectProperty(
t.identifier("styles"),
t.assignmentPattern(
t.identifier("styles"),
t.objectExpression([])
),
false,
true
),
restElement(t.identifier("props"))
])
],
svg
t.arrowFunctionExpression(exportBody, svg)
);
};

// returns
// export default function ${name}(props){ return ${input_svg_node} }
const getNamedExport = function (svg, name) {
return t.exportDefaultDeclaration(
t.functionDeclaration(
t.identifier(name),
exportBody,
t.blockStatement([t.returnStatement(svg)])
)
);
};
Expand All @@ -127,7 +135,7 @@ export default function(babel: BabelCore) {
// add spread props
path.node.attributes.push(t.jSXSpreadAttribute(t.identifier("props")));
}
}
},
};

// converts
Expand All @@ -137,12 +145,20 @@ export default function(babel: BabelCore) {
// export default props => <svg {...props}/>;
// after passing through other visitors
const svgExpressionVisitor = {
ExpressionStatement(path: any) {
ExpressionStatement(path: any, state: any) {
if (!path.get("expression").isJSXElement()) return;
if (path.get("expression.openingElement.name").node.name !== "svg")
return;
path.replaceWith(getExport(path.get("expression").node));
}

path.replaceWith(
state.opts.componentName
? getNamedExport(
path.get("expression").node,
state.opts.componentName
)
: getExport(path.get("expression").node)
);
},
};

const programVisitor = {
Expand All @@ -154,7 +170,7 @@ export default function(babel: BabelCore) {
t.stringLiteral("react")
)
);
}
},
};

return {
Expand All @@ -164,6 +180,6 @@ export default function(babel: BabelCore) {
svgExpressionVisitor,
svgVisitor,
attrVisitor
)
),
};
}
2 changes: 1 addition & 1 deletion packages/react-svg-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-svg-core",
"version": "3.0.3",
"version": "3.1.0",
"description": "Core for react-svg-loader",
"keywords": [
"react-svg-loader"
Expand Down
20 changes: 14 additions & 6 deletions packages/react-svg-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@ export function optimize(opts: any = {}): (content: string) => Promise<string> {
opts = validateAndFix(opts);
const svgo = new Svgo(opts);

return (content: string) => svgo.optimize(content).then(data => data.data);
return (content: string) => svgo.optimize(content).then((data) => data.data);
}

type TransformOpts = { jsx?: boolean; componentName?: string };

// Babel Transform
export function transform({ jsx = false }: { jsx?: boolean } = {}): (
content: string
) => string {
return content =>
export function transform(
opts: TransformOpts = {}
): (content: string) => string {
const jsx = opts.jsx || false;
const componentName = opts.componentName || null;

return (content) =>
babelTransform(content, {
babelrc: false,
configFile: false,
presets: [jsx ? void 0 : require.resolve("@babel/preset-react")].filter(
Boolean
),
plugins: [require.resolve("@babel/plugin-syntax-jsx"), plugin]
plugins: [
require.resolve("@babel/plugin-syntax-jsx"),
[plugin, { componentName }],
],
});
}
1 change: 1 addition & 0 deletions packages/react-svg-loader-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ in the **SAME directory** as the files
## CLI Options

+ `--jsx`: Outputs JSX code instead of compiling it to JavaScript using babel-preset-react
+ `--componentName`: Generating a named function
+ `--stdout`: Outputs to STDOUT
+ `--svgo <config_file>`: Supports SVGO Config YAML / JSON / JS
+ `--svgo.plugins <...plugins>`: Takes in an array of plugins that need to be enabled
Expand Down
2 changes: 1 addition & 1 deletion packages/react-svg-loader-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-svg-loader-cli",
"version": "3.0.3",
"version": "3.1.0",
"description": "react-svg-loader cli",
"keywords": [
"cli",
Expand Down
17 changes: 9 additions & 8 deletions packages/react-svg-loader-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ function getArgv() {
.option("jsx", {
describe: "Output JSX instead of applying babel-preset-react",
boolean: true,
default: false
default: false,
})
.option("stdout", {
describe: "Print output to stdout",
boolean: true,
default: false
default: false,
})
// svgo options
.option("svgo", {
describe: "Path to YAML or JS or JSON config file for SVGO"
describe: "Path to YAML or JS or JSON config file for SVGO",
})
.demand(1)
.version(require("../package.json").version)
Expand All @@ -58,7 +58,7 @@ function getSVGOOpts(argv) {
svgoOpts = argv.svgo;
// convert plugin object to array of objects
if (isPlainObject(svgoOpts.plugins)) {
svgoOpts.plugins = Object.keys(svgoOpts.plugins).map(key => {
svgoOpts.plugins = Object.keys(svgoOpts.plugins).map((key) => {
return { [key]: svgoOpts.plugins[key] === "false" ? false : true };
});
} else if (typeof svgoOpts.plugins === "string") {
Expand All @@ -74,27 +74,28 @@ function getLoaderContext({ argv, query, file }) {
query,
addDependency() {},
async() {
return function(err, result) {
return function (err, result) {
/* eslint-disable no-console */
if (err) console.error("REACT-SVG-LOADER ERROR", file, err.stack);
else if (argv["stdout"]) console.log(result);
else fs.writeFileSync(makeFilename(file), result);
/* eslint-enable */
};
}
},
};
}

function run() {
const argv = getArgv();
const svgoOpts = getSVGOOpts(argv);

argv._.map(file => {
argv._.map((file) => {
const source = fs.readFileSync(file);

const query = {
svgo: svgoOpts,
jsx: argv.jsx
jsx: argv.jsx,
componentName: argv.componentName,
};

loader.apply(getLoaderContext({ argv, query, file }), [source]);
Expand Down
3 changes: 2 additions & 1 deletion packages/react-svg-loader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ By default the loader outputs ES2015 code (with JSX compiled to JavaScript using
{
loader: "react-svg-loader",
options: {
jsx: true // true outputs JSX tags
jsx: true, // true outputs JSX tags
componentName: (filePath) => path.parse(filePath).name, // whether to output a named function, null for anonymous functions
}
}
]
Expand Down
2 changes: 1 addition & 1 deletion packages/react-svg-loader/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-svg-loader",
"version": "3.0.3",
"version": "3.1.0",
"description": "Optimize svg and load it as a React Component",
"keywords": [
"loader",
Expand Down
10 changes: 7 additions & 3 deletions packages/react-svg-loader/src/loader.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import loaderUtils from "loader-utils";
import { optimize, transform } from "react-svg-core";

export default function(content: string) {
export default function (content: string) {
const loaderOpts = loaderUtils.getOptions(this) || {};

const componentName = loaderOpts.componentName
? loaderOpts.componentName(this.resourcePath)
: null;

const cb = this.async();

Promise.resolve(String(content))
.then(optimize(loaderOpts.svgo))
.then(transform({ jsx: loaderOpts.jsx }))
.then(transform({ jsx: loaderOpts.jsx, componentName }))
.then((result: any) => cb(null, result.code))
.catch(err => cb(err));
.catch((err) => cb(err));
}
4 changes: 4 additions & 0 deletions packages/rollup-plugin-react-svg/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ yarn add rollup-plugin-react-svg --dev
```js
// rollup.config.js
import reactSvg from "rollup-plugin-react-svg";
import path from 'path';

export default {
...opts,
Expand All @@ -33,6 +34,9 @@ export default {
// whether to output jsx
jsx: false,

// whether to output a named function
componentName: (filePath) => path.parse(filePath).name,

// include: string
include: null,

Expand Down
2 changes: 1 addition & 1 deletion packages/rollup-plugin-react-svg/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rollup-plugin-react-svg",
"version": "3.0.3",
"version": "3.1.0",
"description": "Optimize svg and load it as a React Component",
"keywords": [
"loader",
Expand Down
9 changes: 7 additions & 2 deletions packages/rollup-plugin-react-svg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type PluginOpts = {
exclude?: any;
svgo?: any;
jsx?: boolean;
componentName?: (filePath: string) => string;
};

export default function reactSvgLoadPlugin(options: PluginOpts = {}): any {
Expand All @@ -20,10 +21,14 @@ export default function reactSvgLoadPlugin(options: PluginOpts = {}): any {

const contents = fs.readFileSync(id);

const componentName = options.componentName
? options.componentName(id)
: null;

return Promise.resolve(String(contents))
.then(optimize(options.svgo))
.then(transform({ jsx: options.jsx }))
.then(transform({ jsx: options.jsx, componentName }))
.then((result: any) => result.code);
}
},
};
}
Loading