Skip to content

Instantly share code, notes, and snippets.

@qramilq
Last active May 12, 2022 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qramilq/b4357b5f278f07b633ba0ecade910db0 to your computer and use it in GitHub Desktop.
Save qramilq/b4357b5f278f07b633ba0ecade910db0 to your computer and use it in GitHub Desktop.
Generating React component's files from command line
const { component, types, barrel } = require('./templates.js');
exports.DEFAULT_COMPONENT_BASE_PATH = './common/components';
exports.FILES_DATA = [
{
getPath: ({ componentName, componentPath }) => `${componentPath}/${componentName}.tsx`, // [component].tsx
getTemplate: component,
},
{
getPath: ({ componentName, componentPath }) => `${componentPath}/${componentName}.types.ts`, // [component].types.ts
getTemplate: types,
},
{
getPath: ({ componentPath }) => `${componentPath}/index.ts`, // index.ts
getTemplate: barrel,
},
{
getPath: ({ componentName, componentPath }) => `${componentPath}/${componentName}.module.css`,
getTemplate: () => '',
},
];
const fs = require('fs');
const rl = require('./readline');
const {
promptNGetComponentName,
promptNGetComponentPath,
createComponentFiles,
} = require('./helpers');
async function start() {
const componentName = await promptNGetComponentName();
const componentPath = await promptNGetComponentPath(componentName);
if (fs.existsSync(componentPath)) {
throw new Error('A component with that name already exists.');
}
fs.mkdirSync(componentPath);
await createComponentFiles(componentPath, componentName);
rl.close();
}
start();
const fs = require('fs');
const rl = require('./readline');
const { FILES_DATA, DEFAULT_COMPONENT_BASE_PATH } = require('./constants');
/**
*
* @returns {Promise<string>}
*/
async function promptNGetComponentName() {
const rawComponentName = await rl.getInputData(`Enter component's name:`);
const componentNameWithCapitalizedFirstLetter =
rawComponentName.charAt(0).toUpperCase() + rawComponentName.slice(1);
return componentNameWithCapitalizedFirstLetter;
}
/**
*
* @param {string} componentName - Component's name will include in path
* @returns {Promise<string>}
*/
async function promptNGetComponentPath(componentName) {
const defaultComponentPath = `${DEFAULT_COMPONENT_BASE_PATH}/${componentName}`;
const rawCoponentPath = await rl.getInputData(
`Enter component's path [${defaultComponentPath}]: `,
);
return rawCoponentPath || defaultComponentPath;
}
/**
*
* @param {string} componentPath
* @param {string} componentName
* @returns {Promise<void[]>}
*/
async function createComponentFiles(componentPath, componentName) {
const writeFileErrorHandler = (err) => {
if (err) throw err;
};
return Promise.all(
FILES_DATA.map(({ getPath, getTemplate }) =>
fs.writeFile(
getPath({ componentPath, componentName }),
getTemplate(componentName),
writeFileErrorHandler,
),
),
);
}
module.exports = {
promptNGetComponentName,
promptNGetComponentPath,
createComponentFiles,
};
const readline = require('readline');
/**
* Readline wrapper
* @class
*/
class ReadLine {
#rl;
static instance;
constructor() {
this.#rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
}
/**
*
* @param {string} question
* @returns {Promise<string>}
*/
getInputData(question) {
return new Promise((resolve) => this.#rl.question(question, resolve));
}
/**
* See {@link readline.Interface.close close() property}.
*/
close() {
this.#rl.close();
}
}
module.exports = new ReadLine();
exports.component = (name) => `import { ${name}Props } from './${name}.types';
import styles from './${name}.module.css';
export function ${name}(props: ${name}Props) {
return <div>${name}Component</div>;
}
`;
exports.types = (name) => `export interface ${name}Props {}
`;
exports.barrel = (name) => `export * from './${name}';
`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment