Skip to content

Commit

Permalink
Publish automation for @fluentui/react-icons (#186)
Browse files Browse the repository at this point in the history
Re-write the SVG->React component conversion to use the svgr node apis, as we were having issues with the npx usage for the cli. This removed the Prettier step but everything else should be conserved. As a bonus we also improved the build-time perf of the conversion.
  • Loading branch information
spencer-nelson authored Feb 24, 2021
1 parent 8d89ae9 commit 4dfa2b5
Show file tree
Hide file tree
Showing 5 changed files with 473 additions and 180 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,23 @@ jobs:
- name: Run build
run: npm run build
working-directory: packages/svg-icons

build-react:
name: Build react library
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Use Node 11
uses: actions/setup-node@v1
with:
node-version: 11.x

- run: npm install
working-directory: importer

- run: |
npm install
npm run build
working-directory: packages/react-icons
18 changes: 18 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ jobs:
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"$NEW_VERSION\"/g" packages/svg-icons/package.json
rm packages/svg-icons/package.json.bk
# Needs to be "-E" instead of "-r" on macOS
- name: Replace version number in react-icons/package.json
run: |
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"$NEW_VERSION\"/g" packages/react-icons/package.json
rm packages/react-icons/package.json.bk
# Needs to be "-E" instead of "-r" on macOS
- name: Replace version number in _manifest.json
run: |
Expand All @@ -137,6 +143,18 @@ jobs:
access: public
package: packages/svg-icons/package.json

- name: Build React library
run: |
npm install
npm run build
working-directory: packages/react-icons

- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
access: public
package: packages/react-icons/package.json

- name: Update icon sheet
run: python3 generate_icons_md.py

Expand Down
107 changes: 107 additions & 0 deletions packages/react-icons/convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

const svgr = require("@svgr/core");
const fs = require("fs");
const path = require("path");
const process = require("process");
const argv = require("yargs").boolean("selector").default("selector", false).argv;
const _ = require("lodash");
const template = require("./template");

const SRC_PATH = argv.source;
const DEST_PATH = argv.dest;

const TSX_EXTENSION = '.tsx'

if (!SRC_PATH) {
throw new Error("Icon source folder not specified by --source");
}
if (!DEST_PATH) {
throw new Error("Output destination folder not specified by --dest");
}

if (!fs.existsSync(DEST_PATH)) {
fs.mkdirSync(DEST_PATH);
}

if (!fs.existsSync(DEST_PATH + '/components')) {
fs.mkdirSync(DEST_PATH + '/components');
}

processFolder(SRC_PATH, DEST_PATH)

/*
Process a folder of svg files and convert them to React components, following naming patterns for the FluentUI System Icons
*/
function processFolder(srcPath, destPath) {
fs.readdir(srcPath, function (err, files) {
if (err) {
console.error("Could not list the directory.", err);
process.exit(1);
}

// These options will be passed to svgr/core
// See https://react-svgr.com/docs/options/ for more info
var svgrOpts = {
template: fileTemplate,
expandProps: 'start', // HTML attributes/props for things like accessibility can be passed in, and will be expanded on the svg object at the start of the object
svgProps: { className: '{className}' }, // In order to provide styling, className will be used
replaceAttrValues: { '#212121': '{primaryFill}' }, // We are designating primaryFill as the primary color for filling. If not provided, it defaults to null.
typescript: true,
}

// Build out the index for the components as we process the files
var indexContents = ''

files.forEach(function (file, index) {
var componentPath = destPath + '/components'
var iconName = file.substr(0, file.length - 4) // strip '.svg'
iconName = iconName.replace("ic_fluent_", "") // strip ic_fluent_
var srcFile = path.join(srcPath, file)
var destFilename = _.camelCase(iconName) // We want them to be camelCase, so access_time would become accessTime here
destFilename = destFilename.replace(destFilename.substring(0, 1), destFilename.substring(0, 1).toUpperCase()) // capitalize the first letter
var destFile = path.join(componentPath, destFilename + TSX_EXTENSION) // get the qualified path

var iconContent = fs.readFileSync(srcFile, { encoding: "utf8" })
jsCode = svgr.default.sync(iconContent, svgrOpts, { filePath: file })
indexContents += '\nexport { default as ' + destFilename + ' } from \'./components/' + destFilename + '\''
fs.writeFileSync(destFile, jsCode, (err) => {
if (err) throw err;
});
});

// Finally add the interface definition and then write out the index.
indexContents += '\nexport { IFluentIconsProps } from \'./IFluentIconsProps.types\''
fs.writeFileSync(destPath + '/index.tsx', indexContents, (err) => {
if (err) throw err;
});
});
}

function fileTemplate(
{ template },
opts,
{ imports, interfaces, componentName, props, jsx, exports }
) {
const plugins = ['jsx', 'typescript']
const tpl = template.smart({ plugins })

componentName.name = componentName.name.substring(3)
componentName.name = componentName.name.replace('IcFluent', '')
exports.declaration.name = componentName.name

return tpl.ast`
import * as React from "react"
import { JSX } from "react-jsx"
import { IFluentIconsProps } from '../IFluentIconsProps.types'
const ${componentName} = (iconProps: IFluentIconsProps, props: React.HTMLAttributes<HTMLElement>) : JSX.Element=> {
const { primaryFill, className } = iconProps;
return ${jsx};
}
${exports}
`
}
Loading

0 comments on commit 4dfa2b5

Please sign in to comment.