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

feat(icons): create a React Icons package #6

Merged
merged 14 commits into from
Feb 6, 2023
Merged
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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"private": true,
"name": "@oxygen-ui/workspace",
"version": "0.1.0",
"description": "WSO2 Design System",
"description": "The Design System powering WSO2 products.",
"author": "WSO2",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/wso2/oxygen/issues"
"url": "https://github.com/wso2/oxygen-ui/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/wso2/oxygen"
"url": "https://github.com/wso2/oxygen-ui"
},
"keywords": [
"wso2",
Expand Down
5 changes: 5 additions & 0 deletions packages/primitives/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@
"@types/node": "^18.11.18",
"@wso2/eslint-plugin": "https://gitpkg.now.sh/brionmario/wso2-ui-configs/packages/eslint-plugin?b46eeeeaa9365277e46aa3b3b6dee339a78f4b3e",
"@wso2/prettier-config": "https://gitpkg.now.sh/brionmario/wso2-ui-configs/packages/prettier-config?b46eeeeaa9365277e46aa3b3b6dee339a78f4b3e",
"cheerio": "1.0.0-rc.12",
"eslint": "8.25.0",
"fs-extra": "^11.1.0",
"globby": "^11.0.0",
"jest": "29.0.3",
"lodash.merge": "^4.6.2",
"prettier": "^2.8.0",
"style-dictionary": "^3.7.1",
"svgson": "^5.2.1",
"token-transformer": "^0.0.28",
"trim-newlines": "^3.0.1",
"typescript": "^4.9.3"
}
}
150 changes: 150 additions & 0 deletions packages/primitives/scripts/build-icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/usr/bin/env node
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
* @fileoverview Build script to generate a JSON file that contains information about input SVGs.
*/

const fs = require('fs-extra');
const path = require('path');
const globby = require('globby');
const cheerio = require('cheerio');
const { parseSync } = require('svgson');
const trimNewlines = require('trim-newlines');
const merge = require('lodash.merge');
const { logger } = require('@oxygen-ui/logger');

const PATHS = {
output: path.resolve(path.join(__dirname, '..', 'dist', 'icons', 'data.json')),
source: {
icons: path.resolve(path.join(__dirname, '..', 'src', 'icons')),
},
};

const filepaths = globby.sync(PATHS.source.icons);
const svgFilepaths = filepaths.filter((filepath) => path.parse(filepath).ext === '.svg');

if (svgFilepaths.length === 0) {
logger.error('No input SVG file(s) found');
process.exit(1);
}

let exitCode = 0;

const icons = svgFilepaths.map((filepath) => {
try {
const filename = path.parse(filepath).base;
const filenamePattern = /(.+)-([0-9]+).svg$/;

if (!filenamePattern.test(filename)) {
throw new Error(
`${filename}: Invalid filename. Please append the height of the SVG to the end of the filename (e.g. alert-16.svg).`,
);
}

const [, name, height] = filename.match(filenamePattern);
const svg = fs.readFileSync(path.resolve(filepath), 'utf8');
const svgElement = cheerio.load(svg)('svg');
const svgWidth = parseInt(svgElement.attr('width'), 10);
const svgHeight = parseInt(svgElement.attr('height'), 10);
const svgViewBox = svgElement.attr('viewBox');
const svgPath = trimNewlines(svgElement.html()).trim();
const ast = parseSync(svg, {
camelcase: true,
});

if (!svgWidth) {
throw new Error(`${filename}: Missing width attribute.`);
}

if (!svgHeight) {
throw new Error(`${filename}: Missing height attribute.`);
}

if (!svgViewBox) {
throw new Error(`${filename}: Missing viewBox attribute.`);
}

if (svgHeight !== parseInt(height, 10)) {
throw new Error(`${filename}: Height in filename does not match height attribute of SVG`);
}

const viewBoxPattern = /0 0 ([0-9]+) ([0-9]+)/;

if (!viewBoxPattern.test(svgViewBox)) {
throw new Error(
`${filename}: Invalid viewBox attribute. The viewBox attribute should be in the following format: "0 0 <width> <height>"`,
);
}

const [, viewBoxWidth, viewBoxHeight] = svgViewBox.match(viewBoxPattern);

if (svgWidth !== parseInt(viewBoxWidth, 10)) {
throw new Error(`${filename}: width attribute and viewBox width do not match.`);
}

if (svgHeight !== parseInt(viewBoxHeight, 10)) {
throw new Error(`${filename}: height attribute and viewBox height do not match.`);
}

return {
ast,
height: svgHeight,
name,
path: svgPath,
width: svgWidth,
};
} catch (error) {
logger.error(error);
// Instead of exiting immediately, we set exitCode to 1 and continue
// iterating through the rest of the SVGs. This allows us to identify all
// the SVGs that have errors, not just the first one. An exit code of 1
// indicates that an error occurred.
// Reference: https://nodejs.org/api/process.html#process_exit_codes
exitCode = 1;
return null;
}
});

// Exit early if any errors occurred.
if (exitCode !== 0) {
process.exit(exitCode);
}

const iconsByName = icons.reduce(
(acc, icon) => merge(acc, {
[icon.name]: {
heights: {
[icon.height]: {
ast: icon.ast,
path: icon.path,
width: icon.width,
},
},
name: icon.name,
},
}),
{},
);

if (PATHS.output) {
fs.outputJsonSync(PATHS.output, iconsByName);
} else {
process.stdout.write(JSON.stringify(iconsByName));
}
116 changes: 116 additions & 0 deletions packages/primitives/scripts/build-sd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env node
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
* @fileoverview Build script to generate the Style Dictionary output.
*/

const fs = require('fs');
const path = require('path');
const StyleDictionary = require('style-dictionary');
const { logger } = require('@oxygen-ui/logger');

const PATHS = {
source: {
tokens: path.resolve(path.join(__dirname, '..', 'src', 'design-tokens')),
},
};

StyleDictionary.registerTransformGroup({
name: 'tokens-es',
transforms: ['name/cti/pascal', 'size/px', 'color/hex'],
});

StyleDictionary.registerTransformGroup({
name: 'tokens-scss',
transforms: ['name/cti/kebab', 'time/seconds', 'size/px', 'color/css'],
});

StyleDictionary.registerTransformGroup({
name: 'tokens-css',
transforms: ['name/cti/kebab', 'time/seconds', 'size/px', 'color/css'],
});

const getStyleDictionaryConfig = (brand, source) => {
const sourceFileName = path.basename(source);

return {
platforms: {
'web/css': {
buildPath: `dist/design-tokens/web/${brand}/css/`,
files: [
{
destination: sourceFileName.replace('.json', '.css'),
format: 'css/variables',
},
],
prefix: brand,
transformGroup: 'tokens-css',
},
'web/es': {
buildPath: `dist/design-tokens/web/${brand}/es/`,
files: [
{
destination: sourceFileName.replace('.json', '.d.ts'),
format: 'typescript/es6-declarations',
},
{
destination: sourceFileName.replace('.json', '.js'),
format: 'javascript/module-flat',
},
{
destination: sourceFileName.replace('.json', '.es6.js'),
format: 'javascript/es6',
},
],
prefix: brand,
transformGroup: 'tokens-es',
},
'web/scss': {
buildPath: `dist/design-tokens/web/${brand}/scss/`,
files: [
{
destination: sourceFileName.replace('.json', '.scss'),
format: 'scss/variables',
},
],
prefix: brand,
transformGroup: 'tokens-scss',
},
},
source: [source],
};
};

fs.readdirSync(PATHS.source.tokens)
.forEach((brand) => {
logger.info(`Processing the Brand: [ ${brand} ]`);

fs.readdirSync(path.join(PATHS.source.tokens, brand))
.forEach((tokenFile) => {
const filePath = path.join(PATHS.source.tokens, brand, tokenFile);

const StyleDictionaryExtended = StyleDictionary
.extend(getStyleDictionaryConfig(brand, filePath));

StyleDictionaryExtended.buildPlatform('web/es');
StyleDictionaryExtended.buildPlatform('web/css');
StyleDictionaryExtended.buildPlatform('web/scss');
});
});
Loading