crcf2
Version:
Creates single react component folder structure
281 lines (247 loc) • 9.55 kB
JavaScript
const path = require('path');
const program = require('commander');
const chalk = require('chalk');
const logger = require('./logger');
const fs = require('./utils/fileHelpers');
const {
createReactComponent,
createReduxContainer,
createReduxActions,
createReactFunctionalComponent,
createReactNativeComponent,
createTypeScriptReactComponent,
createTypeScriptReactNativeComponent,
createReactComponentWithProps,
createReactFunctionalComponentWithProps,
createReactNativeComponentWithProps,
createIndex,
createIndexForActions,
createTest,
createTestForActions,
createTestForContainer,
} = require('./data/componentData');
const formatPrettier = require('./utils/format');
const stringHelper = require('./utils/stringHelper');
const { getComponentName, getComponentParentFolder } = require('./utils/componentsHelpers.js');
const removeOptionsFromArgs = require('./utils/removeOptionsFromArgs');
const createMultiIndex = require('./utils/createMultiIndex');
const logComponentTree = require('./utils/logComponentTree');
const validateArguments = require('./utils/validateArguments');
const getDefaultConfig = require('./utils/getDefaultConfig');
// Grab provided args
let [, , ...args] = process.argv;
// Get the default config
const defaultConfig = getDefaultConfig();
const config = defaultConfig && defaultConfig.config ? defaultConfig.config : null;
// Set command line interface options for cli
program
.version('0.1.0')
.option('--typescript', 'Creates Typescript component and files')
.option('--nocss', 'No css file')
.option('--notest', 'No test file')
.option('--reactnative', 'Creates React Native components')
.option('--createindex', 'Creates index.js file for multple component imports')
.option('-c, --reduxcontainer', 'Creates Redux container')
.option('-a, --reduxactions', 'Creates Redux Actions')
.option('-f, --functional', 'Creates React stateless functional component')
.option('-j, --jsx', 'Creates the component file with .jsx extension')
.option('-l, --less', 'Adds .less file to component')
.option('-s, --scss', 'Adds .scss file to component')
.option('-p, --proptypes', 'Adds prop-types to component')
.option('-u, --uppercase', 'Component files start on uppercase letter')
.option('-d, --default', 'Uses a default configuration if available')
.parse(process.argv);
// Target directory
let TGT_DIR
const { reduxactions, reduxcontainer } = program
if (reduxactions) {
TGT_DIR = process.cwd() + '/src/Actions';
} else if (reduxcontainer) {
TGT_DIR = process.cwd() + '/src/Containers';
} else {
TGT_DIR = process.cwd() + '/src/Components';
}
// Remove Node process args options
args = removeOptionsFromArgs(args);
/**
* Creates files for component
*
* @param {String} componentName - Component name
* @param {String} componentPath - File system path to component
* @param {String|null} cssFileExt - css file extension (css, less, sass or null)
*/
function createFiles(componentName, componentPath, cssFileExt) {
const {
typescript, reactnative, reduxcontainer, reduxactions, jsx, notest, uppercase, functional, proptypes,
} = program;
return new Promise((resolve) => {
const isTypeScript = typescript || (config && config.includes('typescript'));
const isNative = reactnative || (config && config.includes('reactnative'));
// File extension
const ext = isTypeScript ? 'tsx' : 'js';
const jsxExt = 'jsx';
const indexFile = `index.${ext}`;
let name = componentName;
const isJsxFile = jsx || (config && config.includes('jsx')) || false;
const componentFileName = `${name}.${isJsxFile ? jsxExt : ext}`;
// file names to create
const files = [indexFile, componentFileName];
// Prettier options property
const prettierParser = isTypeScript ? 'typescript' : 'babylon';
if (!notest && !(config && config.includes('notest'))) {
files.push(`${name}.test.${ext}`);
}
// Add css | less | sass file if desired
if (cssFileExt && !isNative) {
files.push(`${name}.${cssFileExt}`);
}
if (uppercase || (config && config.includes('uppercase'))) {
name = stringHelper.capitalizeFirstLetter(name);
for (let i = 0; i < files.length; i += 1) {
if (i !== 0) {
files.splice(i, 1, stringHelper.capitalizeFirstLetter(files[i]));
}
}
}
// Create component folder
fs.createDirectorys(componentPath)
.then(() => {
// Create index.js
const promises = [];
for (let i = 0; i < files.length; i += 1) {
const file = files[i];
const filePath = path.join(componentPath, file);
let data = '';
if (file === indexFile) {
if (reduxactions) {
data = createIndexForActions(name, uppercase || (config && config.includes('uppercase')));
} else {
data = createIndex(name, uppercase || (config && config.includes('uppercase')));
}
promises.push(
fs.writeFileAsync(
filePath,
isTypeScript ? data : formatPrettier(data, prettierParser),
),
);
} else if (file === `${name}.${ext}` || file === `${name}.${jsxExt}`) {
if (functional || (config && config.includes('functional'))) {
data = proptypes || (config && config.includes('proptypes'))
? createReactFunctionalComponentWithProps(name)
: createReactFunctionalComponent(name);
} else if (isTypeScript) {
data = isNative
? createTypeScriptReactNativeComponent(name)
: createTypeScriptReactComponent(name);
} else if (proptypes || (config && config.includes('proptypes'))) {
data = isNative
? createReactNativeComponentWithProps(name)
: createReactComponentWithProps(name);
} else if (reduxcontainer) {
data = createReduxContainer(name);
} else if (reduxactions) {
data = createReduxActions(name);
} else {
data = isNative ? createReactNativeComponent(name) : createReactComponent(name);
}
promises.push(
fs.writeFileAsync(
filePath,
isTypeScript ? data : formatPrettier(data, prettierParser),
),
);
} else if (file.indexOf(`.test.${ext}`) > -1) {
if (reduxactions) {
data = createTestForActions(name, uppercase || (config && config.includes('uppercase')), isTypeScript,);
} else if (reduxcontainer) {
data = createTestForContainer(name, uppercase || (config && config.includes('uppercase')), isTypeScript,);
} else {
data = createTest(name, uppercase || (config && config.includes('uppercase')), isTypeScript,);
}
if (!notest && !(config && config.includes('notest'))) {
promises.push(
fs.writeFileAsync(
filePath,
isTypeScript ? data : formatPrettier(data, prettierParser),
),
);
}
} else if (
(
file.indexOf('.css') > -1
|| file.indexOf('.less') > -1
|| file.indexOf('.scss') > -1
) && !(reduxactions || reduxcontainer)
) {
promises.push(fs.writeFileAsync(filePath, ''));
}
}
Promise.all(promises).then(() => resolve(files));
})
.catch((e) => {
logger.error(e);
process.exit();
});
});
}
/**
* Initializes create react component
*/
function initialize() {
// Start timer
/* eslint-disable no-console */
console.time('✨ Finished in');
const promises = [];
// Set component name, path and full path
const componentPath = path.join(TGT_DIR, args[0]);
const folderPath = getComponentParentFolder(componentPath);
if (program.createindex === true) {
createMultiIndex(componentPath);
return;
}
const isValidArgs = validateArguments(args, program);
if (!isValidArgs) {
return;
}
fs.existsSyncAsync(componentPath)
.then(() => {
logger.animateStart('Creating components files...');
let cssFileExt = 'css';
if (program.less || (config && config.includes('less'))) {
cssFileExt = 'less';
}
if (program.scss || (config && config.includes('scss'))) {
cssFileExt = 'scss';
}
if (program.nocss || (config && config.includes('nocss'))) {
cssFileExt = null;
}
for (let i = 0; i < args.length; i += 1) {
const name = getComponentName(args[i]);
promises.push(createFiles(name, folderPath + name, cssFileExt));
}
return Promise.all(promises);
})
.then((filesArrData) => {
logger.log(chalk.cyan('Created new stuff at: '));
// Logs component file tree
logComponentTree(filesArrData, folderPath);
logger.log();
// Stop animating in console
logger.animateStop();
// Stop timer
console.timeEnd('✨ Finished in');
// Log output to console
logger.done('Success!');
})
.catch((error) => {
if (error.message === 'false') {
logger.error(`Folder already exists at ..${componentPath}`);
return;
}
logger.error(error);
});
}
// Start script
initialize();