diff --git a/tools/ui-components/package.json b/tools/ui-components/package.json index 4dfd803784..3bfbd6f4b8 100644 --- a/tools/ui-components/package.json +++ b/tools/ui-components/package.json @@ -60,6 +60,7 @@ "build-storybook": "build-storybook", "build": "cross-env NODE_ENV=production rollup -c", "dev": "cross-env NODE_ENV=development rollup -c -w", - "clean": "rimraf dist/*" + "clean": "rimraf dist/*", + "gen-component": "ts-node ./utils/gen-component-script" } } diff --git a/tools/ui-components/tsconfig.json b/tools/ui-components/tsconfig.json index 276e8af6ca..815279307b 100644 --- a/tools/ui-components/tsconfig.json +++ b/tools/ui-components/tsconfig.json @@ -10,5 +10,11 @@ "strict": true, "noEmit": true, "typeRoots": ["../../node_modules/@types"] + }, + "ts-node": { + "compilerOptions": { + "module": "commonjs" + }, + "transpileOnly": true } } diff --git a/tools/ui-components/utils/gen-component-script.ts b/tools/ui-components/utils/gen-component-script.ts new file mode 100644 index 0000000000..18250a1947 --- /dev/null +++ b/tools/ui-components/utils/gen-component-script.ts @@ -0,0 +1,70 @@ +import fs from 'fs'; +import path from 'path'; +import { component, story, test, barrel, type } from './gen-component-template'; + +// Grab component name from terminal argument +const [name] = process.argv.slice(2); +if (!name) { + throw new Error('You must include a component name.'); +} + +if (!name.match(/^[A-Z]/)) { + throw new Error('Component name must be in PascalCase.'); +} + +const toKebabCase = (pascalCasedName: string) => + pascalCasedName + .replace(/([A-Z][a-z])/g, '-$1') // Add a hyphen before each capital letter + .toLowerCase() + .substring(1); // Return the string but exclude the hyphen at the beginning + +const kebabCasedName = toKebabCase(name); + +const dir = path.join(__dirname, `../src/${kebabCasedName}`); + +// Throw an error if the component's folder already exists +if (fs.existsSync(dir)) { + throw new Error('A component with that name already exists.'); +} + +// Create the folder +fs.mkdirSync(dir); + +const writeFileErrorHandler = (err: Error | null) => { + if (err) { + throw err; + } +}; + +// Create the component file - my-component.tsx +fs.writeFile( + `${dir}/${kebabCasedName}.tsx`, + component(name), + writeFileErrorHandler +); + +// Create the type file - types.ts +fs.writeFile(`${dir}/types.ts`, type(name), writeFileErrorHandler); + +// Create the test file - my-component.test.tsx +fs.writeFile( + `${dir}/${kebabCasedName}.test.tsx`, + test(name), + writeFileErrorHandler +); + +// Create the Storybook file - my-component.stories.tsx +fs.writeFile( + `${dir}/${kebabCasedName}.stories.tsx`, + story(name), + writeFileErrorHandler +); + +// Create the barrel file - index.ts +fs.writeFile( + `${dir}/index.ts`, + barrel(name, kebabCasedName), + writeFileErrorHandler +); + +console.log(`The ${name} component has been created successfully! 🎉`); diff --git a/tools/ui-components/utils/gen-component-template.ts b/tools/ui-components/utils/gen-component-template.ts new file mode 100644 index 0000000000..70f268903e --- /dev/null +++ b/tools/ui-components/utils/gen-component-template.ts @@ -0,0 +1,59 @@ +// component.tsx +export const component = (name: string) => ` +import React from 'react'; + +import { ${name}Props } from './types'; + +export const ${name} = ({}: ${name}Props) => { + return