Skip to content

Commit

Permalink
Icons as single files
Browse files Browse the repository at this point in the history
  • Loading branch information
epessina committed Jul 20, 2024
1 parent 876b0f4 commit 87ce731
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 59 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
- run: yarn
- run: yarn lint
- run: yarn build
- if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: yarn build-icons
- run: yarn test
- name: Coveralls
uses: coverallsapp/github-action@v2
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ yarn build-icons

This command launches a script that copies a set of icon packs from the dist of the [react-icons](https://github.com/react-icons/react-icons) dependency and build the set of Mia-Platform icons from the files contained in `/src/assets/icons`. To add a new icon you just need to place a new `.svg` file here (and don't forget to update the `/src/assets/icons/icons.stories.mdx` story file).

Given the high number of files, the script is pretty slow: Therefore, in CI it is run only on tags.

## License

All files under the src folder must have the license boilerplate attached to files. This is checked by CI.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
"dev": "vite",
"preview": "vite preview",
"build-icons": "node --no-warnings --loader=ts-node/esm scripts/build-icons.ts",
"build-lib": "rm -rf ./dist -rf && tsc && vite build",
"build": "yarn build-icons && yarn build-lib",
"build": "rm -rf ./dist -rf && tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives",
"lint:fix": "yarn lint --fix",
"checkonly": "find ./src -name '*.test.tsx' | xargs grep '\\.only'; test $? -eq 123",
Expand Down Expand Up @@ -102,6 +101,7 @@
"react-dom": "^18.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"storybook": "^7.6.17",
"tar": "^7.4.0",
"traverse": "^0.6.8",
"ts-jest": "^29.1.5",
"ts-node": "^10.9.2",
Expand Down
105 changes: 55 additions & 50 deletions scripts/build-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,23 @@
/* eslint-disable no-console */

import { Cheerio, Element, load } from 'cheerio'
import { type TarOptionsWithAliasesAsyncNoFile, extract } from 'tar'
import { IconTree } from 'react-icons'
import camelcase from 'camelcase'
import { createRequire } from 'module'
import fs from 'fs/promises'
import { glob } from 'glob'
import https from 'https'
import path from 'path'
import url from 'url'

const require = createRequire(import.meta.url)
const reactIconsPath = path.dirname(require.resolve('react-icons/package.json'))
const REACT_ICONS_VERSION = '5.2.1'
const REACT_ICONS_PACKAGES = ['lib', 'ai', 'fi', 'pi']

const dirname = path.dirname(url.fileURLToPath(import.meta.url))
const rootDir = path.resolve(dirname, '..')
const outDir = path.resolve(rootDir, 'icons')

const reactIconsPackages = ['ai', 'fi', 'pi']

async function initialize(): Promise<void> {
async function setup(): Promise<void> {
const outDirContent = await fs.readdir(outDir)

const promises = outDirContent.reduce<Promise<void>[]>((acc, cur) => {
Expand All @@ -51,17 +50,36 @@ async function initialize(): Promise<void> {
await Promise.all(promises)
}

async function copyReactIconsPackages(): Promise<void> {
const packagesToCopy = ['lib', ...reactIconsPackages]
async function downloadReactIconsPackage(): Promise<void> {
const pkgUrl = `https://github.com/react-icons/react-icons/releases/download/v${REACT_ICONS_VERSION}/react-icons-all-files-${REACT_ICONS_VERSION}.tgz`

const tarOptions: TarOptionsWithAliasesAsyncNoFile = {
cwd: outDir,
filter: (entryPath) => REACT_ICONS_PACKAGES.some((pkgName) => entryPath.startsWith(`package/${pkgName}`)),
strip: 1,
}

const urlToCall = await new Promise<string>((resolve, reject) => {
const reqUrl = new url.URL(pkgUrl)

const promises = packagesToCopy.map((packageName) => {
const source = path.resolve(reactIconsPath, packageName)
const dest = path.resolve(outDir, packageName)
const request = https.get(reqUrl, (res) => resolve(res.headers.location ?? ''))

return fs.cp(source, dest, { recursive: true })
request.on('error', reject)
})

await Promise.all(promises)
await new Promise<void>((resolve, reject) => {
const request = https.get(urlToCall)

request.on('response', (res) => {
res.pipe(extract(tarOptions), { end: true })

res.on('error', reject)

res.on('end', resolve)
})

request.on('error', reject)
})
}

async function svgStringToTree(svg: string): Promise<IconTree> {
Expand Down Expand Up @@ -105,35 +123,6 @@ async function buildMiaIcons(): Promise<void> {
const miaIconsPackageDir = path.resolve(outDir, 'mi')
await fs.mkdir(miaIconsPackageDir)

const packageJsonPath = path.resolve(miaIconsPackageDir, 'package.json')
const esmIndexPath = path.resolve(miaIconsPackageDir, 'index.mjs')
const cjsIndexPath = path.resolve(miaIconsPackageDir, 'index.js')
const declarationsPath = path.resolve(miaIconsPackageDir, 'index.d.ts')

await fs.appendFile(
packageJsonPath,
'{\n "sideEffects": false,\n "module": "./index.esm.js"\n}',
'utf-8'
)

await fs.appendFile(
esmIndexPath,
'// THIS FILE IS AUTO GENERATED\nimport { GenIcon } from \'../lib\';\n',
'utf-8'
)

await fs.appendFile(
cjsIndexPath,
'// THIS FILE IS AUTO GENERATED\nvar GenIcon = require(\'../lib\').GenIcon\n',
'utf-8'
)

await fs.appendFile(
declarationsPath,
'// THIS FILE IS AUTO GENERATED\nimport { IconType } from \'../lib\'\n',
'utf-8'
)

const svgFiles = await glob(path.resolve(rootDir, 'src/assets/icons/*.svg'))

const promises = svgFiles.map(async(filePath) => {
Expand All @@ -145,28 +134,44 @@ async function buildMiaIcons(): Promise<void> {
const iconTree = await svgStringToTree(svgStr)
const iconTreeStr = JSON.stringify(iconTree)

const esmTemplate = `export function ${iconName} (props) {\n`
const esmFilePath = path.resolve(miaIconsPackageDir, `${iconName}.mjs`)
const esmTemplate = `// THIS FILE IS AUTO GENERATED\n`
+ `import { GenIcon } from '../lib/index.mjs';\n`
+ `export function ${iconName} (props) {\n`
+ ` return GenIcon(${iconTreeStr})(props);\n`
+ `};\n`

const cjsTemplate = `module.exports.${iconName} = function ${iconName} (props) {\n`
const cjsFilePath = path.resolve(miaIconsPackageDir, `${iconName}.js`)
const cjsTemplate = `// THIS FILE IS AUTO GENERATED\n`
+ `var GenIcon = require('../lib').GenIcon\n`
+ `module.exports.${iconName} = function ${iconName} (props) {\n`
+ ` return GenIcon(${iconTreeStr})(props);\n`
+ `};\n`

const declarationsTemplate = `export declare const ${iconName}: IconType;\n`
const declarationsFilePath = path.resolve(miaIconsPackageDir, `${iconName}.d.ts`)
const declarationsTemplate = `// THIS FILE IS AUTO GENERATED\n`
+ `import { IconType } from '../lib/index.mjs'\n`
+ `export declare const ${iconName}: IconType;\n`

await fs.appendFile(esmIndexPath, esmTemplate, 'utf-8')
await fs.appendFile(cjsIndexPath, cjsTemplate, 'utf-8')
await fs.appendFile(declarationsPath, declarationsTemplate, 'utf-8')
await fs.writeFile(esmFilePath, esmTemplate, 'utf-8')
await fs.writeFile(cjsFilePath, cjsTemplate, 'utf-8')
await fs.writeFile(declarationsFilePath, declarationsTemplate, 'utf-8')
})

await Promise.all(promises)
}

async function main(): Promise<void> {
await initialize()
await copyReactIconsPackages()
console.info(`» Start icons building process`)

await setup()

console.debug(`» Downloading icons from react-icons v${REACT_ICONS_VERSION} archive...`)
await downloadReactIconsPackage()
console.debug('» Icon files downloaded and extracted')

await buildMiaIcons()
console.debug('» Mia-Platform icons built')
}

main()
Expand Down
2 changes: 1 addition & 1 deletion src/assets/icons/icons.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ You can use these icons with the [`<Icon />` component](/docs/components-icon--d
## Import

```tsx
import { IconName } from "@mia-platform-internal/console-design-system-react/icons/mi"
import { IconName } from "@mia-platform-internal/console-design-system-react/icons/mi/IconName"
```

## Icons
Expand Down
10 changes: 5 additions & 5 deletions src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ import { IconProps } from './Icon.props'
* pack such as [React Icons](https://react-icons.github.io/react-icons/).
*
* For convenience, the design system itself ships several icon packs ready to use with this components, namely:
* - [Ant Design Icons](https://react-icons.github.io/react-icons/icons/ai/) importable from `@mia-platform-internal/console-design-system-react/icons/ai`
* - [Phosphor Icons](https://react-icons.github.io/react-icons/icons/pi/) importable from `@mia-platform-internal/console-design-system-react/icons/pi`
* - [Feather Icons](https://react-icons.github.io/react-icons/icons/fi/) importable from `@mia-platform-internal/console-design-system-react/icons/fi`
* - [Ant Design Icons](https://react-icons.github.io/react-icons/icons/ai/) importable from `@mia-platform-internal/console-design-system-react/icons/ai/<name-of-the-icon>`
* - [Phosphor Icons](https://react-icons.github.io/react-icons/icons/pi/) importable from `@mia-platform-internal/console-design-system-react/icons/pi/<name-of-the-icon>`
* - [Feather Icons](https://react-icons.github.io/react-icons/icons/fi/) importable from `@mia-platform-internal/console-design-system-react/icons/fi/<name-of-the-icon>`
* - [Mia-Platform Icons](/docs/icons-mia-platform--docs) importable from
* `@mia-platform-internal/console-design-system-react/icons/mi`
* `@mia-platform-internal/console-design-system-react/icons/mi/<name-of-the-icon>`
*
* To use one of the aforementioned icons, just import the component and pass it to `<Icon />`:
*
* ```tsx
* import { PiAddressBook } from "@mia-platform-internal/console-design-system-react/icons/pi"
* import { PiAddressBook } from "@mia-platform-internal/console-design-system-react/icons/pi/PiAddressBook"
*
* const App = () => <Icon component={PiAddressBook} />
* ```
Expand Down
93 changes: 92 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,15 @@ __metadata:
languageName: node
linkType: hard

"@isaacs/fs-minipass@npm:^4.0.0":
version: 4.0.1
resolution: "@isaacs/fs-minipass@npm:4.0.1"
dependencies:
minipass: "npm:^7.0.4"
checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5
languageName: node
linkType: hard

"@istanbuljs/load-nyc-config@npm:^1.0.0":
version: 1.1.0
resolution: "@istanbuljs/load-nyc-config@npm:1.1.0"
Expand Down Expand Up @@ -3198,6 +3207,7 @@ __metadata:
react-icons: "npm:^4.11.0"
rollup-plugin-visualizer: "npm:^5.12.0"
storybook: "npm:^7.6.17"
tar: "npm:^7.4.0"
traverse: "npm:^0.6.8"
ts-jest: "npm:^29.1.5"
ts-node: "npm:^10.9.2"
Expand Down Expand Up @@ -7740,6 +7750,13 @@ __metadata:
languageName: node
linkType: hard

"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c
languageName: node
linkType: hard

"ci-info@npm:^3.2.0":
version: 3.9.0
resolution: "ci-info@npm:3.9.0"
Expand Down Expand Up @@ -10228,6 +10245,22 @@ __metadata:
languageName: node
linkType: hard

"glob@npm:^10.3.7":
version: 10.4.5
resolution: "glob@npm:10.4.5"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^3.1.2"
minimatch: "npm:^9.0.4"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^1.11.1"
bin:
glob: dist/esm/bin.mjs
checksum: 10/698dfe11828b7efd0514cd11e573eaed26b2dff611f0400907281ce3eab0c1e56143ef9b35adc7c77ecc71fba74717b510c7c223d34ca8a98ec81777b293d4ac
languageName: node
linkType: hard

"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0":
version: 7.2.3
resolution: "glob@npm:7.2.3"
Expand Down Expand Up @@ -12800,7 +12833,7 @@ __metadata:
languageName: node
linkType: hard

"minipass@npm:^7.0.2, minipass@npm:^7.1.2":
"minipass@npm:^7.0.2, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
version: 7.1.2
resolution: "minipass@npm:7.1.2"
checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950
Expand All @@ -12817,6 +12850,16 @@ __metadata:
languageName: node
linkType: hard

"minizlib@npm:^3.0.1":
version: 3.0.1
resolution: "minizlib@npm:3.0.1"
dependencies:
minipass: "npm:^7.0.4"
rimraf: "npm:^5.0.5"
checksum: 10/622cb85f51e5c206a080a62d20db0d7b4066f308cb6ce82a9644da112367c3416ae7062017e631eb7ac8588191cfa4a9a279b8651c399265202b298e98c4acef
languageName: node
linkType: hard

"mkdirp-classic@npm:^0.5.2":
version: 0.5.3
resolution: "mkdirp-classic@npm:0.5.3"
Expand Down Expand Up @@ -12844,6 +12887,15 @@ __metadata:
languageName: node
linkType: hard

"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
bin:
mkdirp: dist/cjs/src/bin.js
checksum: 10/16fd79c28645759505914561e249b9a1f5fe3362279ad95487a4501e4467abeb714fd35b95307326b8fd03f3c7719065ef11a6f97b7285d7888306d1bd2232ba
languageName: node
linkType: hard

"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
Expand Down Expand Up @@ -13400,6 +13452,13 @@ __metadata:
languageName: node
linkType: hard

"package-json-from-dist@npm:^1.0.0":
version: 1.0.0
resolution: "package-json-from-dist@npm:1.0.0"
checksum: 10/ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea
languageName: node
linkType: hard

"pako@npm:~0.2.0":
version: 0.2.9
resolution: "pako@npm:0.2.9"
Expand Down Expand Up @@ -15193,6 +15252,17 @@ __metadata:
languageName: node
linkType: hard

"rimraf@npm:^5.0.5":
version: 5.0.9
resolution: "rimraf@npm:5.0.9"
dependencies:
glob: "npm:^10.3.7"
bin:
rimraf: dist/esm/bin.mjs
checksum: 10/443669809ca3d402ca7565fd9f5b994b5669d8f8b33a23e3a00a66c3a2e4c529d8a5a47c9e7c42f2c7a0c70d21ff8bb1c86493b12027139a3de47fc33fe60084
languageName: node
linkType: hard

"rimraf@npm:~2.6.2":
version: 2.6.3
resolution: "rimraf@npm:2.6.3"
Expand Down Expand Up @@ -16134,6 +16204,20 @@ __metadata:
languageName: node
linkType: hard

"tar@npm:^7.4.0":
version: 7.4.0
resolution: "tar@npm:7.4.0"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.0.1"
mkdirp: "npm:^3.0.1"
yallist: "npm:^5.0.0"
checksum: 10/fd0e366c0da823ac66cd0d2f52ff2b7963945a4252db88b5887053b6ab54a489380dfad64b2b26ae4a08bb2f5d66de04432d45e039a37bc0d442546ef29c6ee6
languageName: node
linkType: hard

"telejson@npm:^7.2.0":
version: 7.2.0
resolution: "telejson@npm:7.2.0"
Expand Down Expand Up @@ -17528,6 +17612,13 @@ __metadata:
languageName: node
linkType: hard

"yallist@npm:^5.0.0":
version: 5.0.0
resolution: "yallist@npm:5.0.0"
checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a
languageName: node
linkType: hard

"yaml@npm:^1.10.2":
version: 1.10.2
resolution: "yaml@npm:1.10.2"
Expand Down

0 comments on commit 87ce731

Please sign in to comment.