Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
143 changes: 66 additions & 77 deletions generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,79 @@ const fs = require('fs')
const path = require('path')
const axios = require('axios')

// Base url for icons
const iconURL = 'https://fonts.google.com/metadata/icons'
const GOOGLE_FONTS_URL = 'https://fonts.google.com/metadata/icons'
const ICON_FAMILIES = [
{ id: 'materialicons', postfix: '' },
{ id: 'materialiconsoutlined', postfix: 'Outlined' },
{ id: 'materialiconsround', postfix: 'Round' },
{ id: 'materialiconssharp', postfix: 'Sharp' },
{ id: 'materialiconstwotone', postfix: 'TwoTone' },
]

function setup() {
;(async () => {
generateIconPropsFile()

const res = await axios.get(GOOGLE_FONTS_URL)

// remove )]}' from the response
const data = res.data.substring(4)
const icons = await JSON.parse(data)

for (let i = 0; i < icons.icons.length; i++) {
generateComponents(icons.icons[i])
}
})()

function generateIconPropsFile() {
Comment thread
stplasim marked this conversation as resolved.
Outdated
const typesFile = `
import React from 'react'
export interface IconProps extends React.SVGProps<SVGSVGElement> {
title?: string
}
`

// Create types file
fs.writeFileSync(path.join(__dirname, 'src', 'types.ts'), typesFile)
}

/**
* Format name.
* Covert from snake case to camel case and prevent numbers at the beginning
*
* @param string - String to format
* @returns {string} - Formatted string
*/
function formatName(string) {
async function generateComponents(icon) {
Comment thread
stplasim marked this conversation as resolved.
Outdated
for (let i = 0; i < ICON_FAMILIES.length; i++) {
await generateComponent(icon, ICON_FAMILIES[i])
}
}

async function generateComponent(icon, family) {
try {
const name = formatName(icon.name, family.postfix)
const svg = await downloadSVG(icon.name, family.id, icon.version)

console.log(`Downloading ${name}`)

await fs.writeFileSync(
path.join(__dirname, 'src', `${name}.tsx`),
mapSVGToTemplate(name, svg)
)
} catch {
process.abort()
}
}

function formatName(string, familyPostfix) {
const formattedString = string
.replace(/_/g, ' ')
.replace(/\w\S*/g, (txt) => {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
})
.replace(/ /g, '')
return 'Icon' + formattedString
}

/**
* Component template for functional react icon component
*
* @param name - Component name
* @param svg - Icon SVG
* @returns {string} - Component
*/
function componentTemplate(name, svg) {
return `
import React from 'react'
import { IconProps } from './types'

const ${name}: React.FC<IconProps> = ({ ...props }) => (
${svg}
)

export { ${name} as default }
`
return 'Icon' + formattedString + familyPostfix
}

/**
* Generate React Icon components
*
* @param icon - Icon object
*/
function generateComponent(icon) {
icon.forEach(async (c) => {
const name = formatName(c.name)
const svg = await getSVGFile(c.name, c.version)

fs.writeFileSync(
path.join(__dirname, 'src', `${name}.tsx`),
componentTemplate(name, svg)
async function downloadSVG(icon, familyId, version) {
const svg = await axios
.get(
`https://fonts.gstatic.com/s/i/${familyId}/${icon}/v${version}/24px.svg`
)
})
}

/**
* Get SVG data form google static fonts api
*
* @param icon - Icon name
* @param version - Icon Version
* @returns {Promise<any>} - SVG Data
*/
async function getSVGFile(icon, version) {
const svg = await axios.get(
`https://fonts.gstatic.com/s/i/materialicons/${icon}/v${version}/24px.svg`
)
.catch((err) => console.log(err))

return svg.data
.replace('height="24"', '')
Expand All @@ -93,23 +86,19 @@ async function getSVGFile(icon, version) {
.replace('>', '>{props.title && <title>{props.title}</title>}')
.replace(/style="enableBackground:(.*?);?"/g, 'enableBackground="$1"')
.replace(/style="fill:(.*?);?"/g, 'fill="$1"')
.replace(/xmlns:xlink/g, 'xmlnsXlink')
.replace(/xlink:href/g, 'xlinkHref')
}

/**
* Main function
*/
;(async () => {
// Setup source folder
setup()

// Get icon information from google fonts
const res = await axios.get(iconURL)
// remove )]}' from the response
const data = res.data.substring(4)

// Parse response from json to js object
const icons = await JSON.parse(data)

// Generate icon components and Index
await generateComponent(icons.icons)
})()
function mapSVGToTemplate(name, svg) {
return `
import React from 'react'
import { IconProps } from './types'

const ${name}: React.FC<IconProps> = ({ ...props }) => (
${svg}
)

export { ${name} as default }
`
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"scripts": {
"build": "tsc",
"generate": "rm -rf src/* && node generator.js",
"clear": "rm -rf src/*",
"generate": "node generator.js",
"lint": "eslint --ext js,ts,tsx src ./generator.js",
"lint:fix": "npm run lint -- --fix",
"typecheck": "tsc --noEmit",
Expand Down
12 changes: 10 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@ import IconCached from '@aboutbits/react-material-icons/dist/IconCached'

const MyCommponent = () => {
return (
<IconCached />
);
<IconCached/>
);
}
```

SVG related parameters like height and width can be passed as props.

To import the different variants of an icon you can add the variants as a postfix:

- `Icon10k` > Default filled
- `Icon10kOutlined` > Outlined
- `Icon10kRound` > Rounded
- `Icon10kSharp` > Sharp
- `Icon10kTwoTone` > Tow tone

## Generate Components

To generate the components, simply run the following command:
Expand Down