From ebb1feee50da05f905ca9a36fa1783c7e835f1b0 Mon Sep 17 00:00:00 2001 From: Kerry Date: Wed, 9 Feb 2022 10:33:21 +0100 Subject: [PATCH] Basic script to create react component with test and style files (#7757) * basically working script Signed-off-by: Kerry Archibald * add test template * add skinned-sdk import to test temp * remove extra import Signed-off-by: Kerry Archibald * comments Signed-off-by: Kerry Archibald --- package.json | 1 + scripts/make-react-component.js | 135 ++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100755 scripts/make-react-component.js diff --git a/package.json b/package.json index a5e536e0bb..147675ce28 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "prunei18n": "matrix-prune-i18n", "diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json", "reskindex": "node scripts/reskindex.js -h header", + "make-component": "node scripts/make-react-component.js", "reskindex:watch": "node scripts/reskindex.js -h header -w", "rethemendex": "res/css/rethemendex.sh", "clean": "rimraf lib", diff --git a/scripts/make-react-component.js b/scripts/make-react-component.js new file mode 100755 index 0000000000..063914fafe --- /dev/null +++ b/scripts/make-react-component.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +const fs = require('fs/promises'); +const path = require('path'); + +/** + * Unsophisticated script to create a styled, unit-tested react component. + * -filePath / -f : path to the component to be created, including new component name, excluding extension, relative to src + * -withStyle / -s : optional, flag to create a style file for the component. Defaults to false. + * + * eg: + * ``` + * node srcipts/make-react-component.js -f components/toasts/NewToast -s + * ``` + * creates files: + * - src/components/toasts/NewToast.tsx + * - test/components/toasts/NewToast-test.tsx + * - res/css/components/toasts/_NewToast.scss + * + */ + +const TEMPLATES = { + COMPONENT: ` +import React from 'react'; + +interface Props {} + +const %%ComponentName%%: React.FC = () => { + return
; +}; + +export default %%ComponentName%%; +`, + TEST: ` +import React from 'react'; +import { mount } from 'enzyme'; + +import '%%SkinnedSdkPath%%'; +import %%ComponentName%% from '%%RelativeComponentPath%%'; + +describe('<%%ComponentName%% />', () => { + const defaultProps = {}; + const getComponent = (props = {}) => + mount(<%%ComponentName%% {...defaultProps} {...props} />); + + it('renders', () => { + const component = getComponent(); + expect(component).toBeTruthy(); + }); +}); +`, + STYLE: ` +.mx_%%ComponentName%% { + +} +` +} + + +const options = { + alias: { + filePath: 'f', + withStyle: 's' + } +} + +const args = require('minimist')(process.argv, options); + +const ensureDirectoryExists = async (filePath) => { + const dirName = path.parse(filePath).dir; + + try { + await fs.access(dirName); + return; + } catch (error) { } + + await fs.mkdir(dirName, { recursive: true }) +} + +const makeFile = async ({ + filePath, componentName, extension, base, template, prefix, componentFilePath +}) => { + const newFilePath = path.join(base, path.dirname(filePath), `${prefix || ''}${path.basename(filePath)}${extension}`) + await ensureDirectoryExists(newFilePath); + + const relativePathToComponent = path.parse(path.relative(path.dirname(newFilePath), componentFilePath || '')); + const importComponentPath = path.join(relativePathToComponent.dir, relativePathToComponent.name); + + const skinnedSdkPath = path.relative(path.dirname(newFilePath), 'test/skinned-sdk') + + try { + await fs.writeFile(newFilePath, fillTemplate(template, componentName, importComponentPath, skinnedSdkPath), { flag: 'wx' }); + console.log(`Created ${path.relative(process.cwd(), newFilePath)}`); + return newFilePath; + } catch (error) { + if (error.code === 'EEXIST') { + console.log(`File already exists ${path.relative(process.cwd(), newFilePath)}`); + return newFilePath; + } else { + throw error; + } + } +} + +const fillTemplate = (template, componentName, relativeComponentFilePath, skinnedSdkPath) => + template.replace(/%%ComponentName%%/g, componentName) + .replace(/%%RelativeComponentPath%%/g, relativeComponentFilePath) + .replace(/%%SkinnedSdkPath%%/g, skinnedSdkPath) + + +const makeReactComponent = async () => { + const { filePath, withStyle } = args; + + if (!filePath) { + throw new Error('No file path provided, did you forget -f?') + } + + const componentName = filePath.split('/').slice(-1).pop(); + + const componentFilePath = await makeFile({ filePath, componentName, base: 'src', extension: '.tsx', template: TEMPLATES.COMPONENT }); + await makeFile({ filePath, componentFilePath, componentName, base: 'test', extension: '-test.tsx', template: TEMPLATES.TEST, componentName }); + if (withStyle) { + await makeFile({ filePath, componentName, base: 'res/css', prefix: '_', extension: '.scss', template: TEMPLATES.STYLE }); + } +} + +// Wrapper since await at the top level is not well supported yet +function run() { + (async function () { + await makeReactComponent(); + })(); +} + +run(); +return; +