diff --git a/src/helpers.js b/src/helpers/index.js similarity index 70% rename from src/helpers.js rename to src/helpers/index.js index 89f540b..383bf9d 100644 --- a/src/helpers.js +++ b/src/helpers/index.js @@ -12,7 +12,7 @@ const os = require('os'); const prettier = require('prettier'); const chalk = require('chalk'); -const { requireOptional } = require('./utils'); +const { requireOptional } = require('../utils'); // Get the configuration for this component. @@ -30,6 +30,7 @@ module.exports.getConfig = () => { const defaults = { type: 'class', + styling: 'none', dir: 'src/components', extension: 'js' }; @@ -46,6 +47,24 @@ module.exports.buildPrettifier = prettierConfig => text => ( prettier.format(text, prettierConfig) ); +const componentTypes = { + FUNCTIONAL: 'functional', + CLASS: 'class', + PURE_CLASS: 'pure-class', +}; +module.exports.componentTypes = componentTypes; + +const stylingTypes = { + STYLED: 'styled-components', + CSS_MODULES: 'css-modules', + APHRODITE: 'aphrodite', +}; +module.exports.stylingTypes = stylingTypes; + +module.exports.hasCssFile = styling => { + return styling === module.exports.stylingTypes.CSS_MODULES; +} + // Emit a message confirming the creation of the component const colors = { red: [216, 16, 16], @@ -57,7 +76,17 @@ const colors = { }; const logComponentType = (selected) => ( - ['class', 'pure-class', 'functional'] + Object.values(componentTypes) + .sort((a, b) => a === selected ? -1 : 1) + .map(option => ( + option === selected + ? `${chalk.bold.rgb(...colors.blue)(option)}` + : `${chalk.rgb(...colors.darkGray)(option)}` + )).join(' ') +); + +const logStylingType = (selected) => ( + Object.values(stylingTypes) .sort((a, b) => a === selected ? -1 : 1) .map(option => ( option === selected @@ -66,17 +95,21 @@ const logComponentType = (selected) => ( )).join(' ') ); -module.exports.logIntro = ({ name, dir, type }) => { +module.exports.logIntro = ({ name, dir, type, styling }) => { console.info('\n'); console.info(`✨ Creating the ${chalk.bold.rgb(...colors.gold)(name)} component ✨`); console.info('\n'); const pathString = chalk.bold.rgb(...colors.blue)(dir); - const typeString = logComponentType(type); + const componentTypeString = logComponentType(type); + const stylingTypeString = logStylingType(styling); console.info(`Directory: ${pathString}`); - console.info(`Type: ${typeString}`); + console.info(`Type: ${componentTypeString}`); + if (styling && styling !== 'none') { + console.info(`Styling: ${stylingTypeString}`); + } console.info(chalk.rgb(...colors.darkGray)('=========================================')); console.info('\n'); diff --git a/src/helpers/templateConstructor.js b/src/helpers/templateConstructor.js new file mode 100644 index 0000000..28e994d --- /dev/null +++ b/src/helpers/templateConstructor.js @@ -0,0 +1,23 @@ +const reactImportTemplates = require('../templates/react-imports'); +const styleImportTemplates = require('../templates/style-imports'); +const renderContentTemplates = require('../templates/render-contents'); +const componentDefinitionTemplates = require('../templates/component-definition'); +const belowComponentTemplates = require('../templates/below-component'); +const exportsTemplates = require('../templates/exports'); + +module.exports = (componentName, componentType, style) => { + const renderContents = renderContentTemplates(style); + const template = ` + ${reactImportTemplates(componentType)} + ${styleImportTemplates(style, componentName)} + + ${componentDefinitionTemplates(componentType, componentName, renderContents)} + + ${belowComponentTemplates(style)} + + ${exportsTemplates(style, componentName)} + `; + + return template; +} + diff --git a/src/index.js b/src/index.js index ae29874..0ce29b9 100755 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ const { logItemCompletion, logConclusion, logError, + hasCssFile, } = require('./helpers'); const { requireOptional, @@ -18,7 +19,8 @@ const { readFilePromiseRelative, writeFilePromise, } = require('./utils'); - +const generateTemplate = require('./helpers/templateConstructor'); +const cssTemplate = require('./templates/css'); // Load our package.json, so that we can pass the version onto `commander`. const { version } = require('../package.json'); @@ -30,6 +32,9 @@ const config = getConfig(); // Convenience wrapper around Prettier, so that config doesn't have to be // passed every time. const prettify = buildPrettifier(config.prettierConfig); +const prettifyCss = buildPrettifier(Object.assign({}, config.prettierConfig, { + parser: 'postcss' +})); program @@ -40,6 +45,11 @@ program 'Type of React component to generate (default: "class")', /^(class|pure-class|functional)$/i, config.type + ).option( + '-s, --styling ', + 'Which styling solution to generate for (default: "none")', + /^(none|styled-components|css-modules|aphrodite)$/i, + config.styling ).option( '-d, --dir ', 'Path to the "components" directory (default: "src/components")', @@ -52,20 +62,23 @@ program const [componentName] = program.args; -// Find the path to the selected template file. -const templatePath = `./templates/${program.type}.js`; - // Get all of our file paths worked out, for the user's project. const componentDir = `${program.dir}/${componentName}`; -const filePath = `${componentDir}/${componentName}.${program.extension}`; +const componentFilePath = `${componentDir}/${componentName}.${program.extension}`; +const cssFilePath = `${componentDir}/${componentName}.${'css'}`; const indexPath = `${componentDir}/index.js`; // Our index template is super straightforward, so we'll just inline it for now. -const indexTemplate = prettify(`\ +const indexTemplate = `\ export { default } from './${componentName}'; -`); +`; -logIntro({ name: componentName, dir: componentDir, type: program.type }); +logIntro({ + name: componentName, + dir: componentDir, + type: program.type, + styling: program.styling, +}); // Check if componentName is provided @@ -91,33 +104,37 @@ if (fs.existsSync(fullPathToComponentDir)) { // Start by creating the directory that our component lives in. mkDirPromise(componentDir) .then(() => ( - readFilePromiseRelative(templatePath) + generateTemplate(componentName, program.type, program.styling) )) .then(template => { logItemCompletion('Directory created.'); return template; }) - .then(template => ( - // Replace our placeholders with real data (so far, just the component name) - template.replace(/COMPONENT_NAME/g, componentName) - )) .then(template => ( // Format it using prettier, to ensure style consistency, and write to file. - writeFilePromise(filePath, prettify(template)) + writeFilePromise(componentFilePath, prettify(template)) )) - .then(template => { + .then(() => { + // Check whether we should make a CSS file too + if (hasCssFile(program.styling)) { + // Format it using prettier, to ensure style consistency, and write to file. + return writeFilePromise(cssFilePath, prettifyCss(cssTemplate)) + .then(() => { + logItemCompletion('CSS file built and saved to disk.'); + }); + } + }) + .then(() => { logItemCompletion('Component built and saved to disk.'); - return template; }) - .then(template => ( + .then(() => ( // We also need the `index.js` file, which allows easy importing. writeFilePromise(indexPath, prettify(indexTemplate)) )) - .then(template => { + .then(() => { logItemCompletion('Index file built and saved to disk.'); - return template; }) - .then(template => { + .then(() => { logConclusion(); }) .catch(err => { diff --git a/src/templates/below-component.js b/src/templates/below-component.js new file mode 100644 index 0000000..0c2a4ea --- /dev/null +++ b/src/templates/below-component.js @@ -0,0 +1,26 @@ +const { stylingTypes } = require('../helpers'); + +const styled = `\ +const Wrapper = styled.div\` +\`; +`; + +const aphrodite = `\ +const styles = StyleSheet.create({ + wrapper: { + }, +}); +`; + +module.exports = (style, componentType) => { + switch (style) { + case stylingTypes.STYLED: + return styled; + + case stylingTypes.APHRODITE: + return aphrodite; + + default: + return ''; + } +} diff --git a/src/templates/class.js b/src/templates/class.js deleted file mode 100644 index 169d2fa..0000000 --- a/src/templates/class.js +++ /dev/null @@ -1,10 +0,0 @@ -import React, { Component } from 'react'; - - -class COMPONENT_NAME extends Component { - render() { - return
; - } -} - -export default COMPONENT_NAME; diff --git a/src/templates/component-definition.js b/src/templates/component-definition.js new file mode 100644 index 0000000..2ed4031 --- /dev/null +++ b/src/templates/component-definition.js @@ -0,0 +1,36 @@ +const { componentTypes } = require('../helpers'); + +const functional = (ComponentName, renderContents) => (` +const ${ComponentName} = () => { + return ${renderContents}; +}; +`) + +const classComp = (ComponentName, renderContents) => (` +class ${ComponentName} extends Component { + render() { + return ${renderContents}; + } +} +`) + +const pureClass = (ComponentName, renderContents) => (` +class ${ComponentName} extends PureComponent { + render() { + return ${renderContents}; + } +} +`) + +module.exports = (componentType, ...args) => { + switch (componentType) { + case componentTypes.FUNCTIONAL: + return functional(...args); + + case componentTypes.CLASS: + return classComp(...args); + + case componentTypes.PURE_CLASS: + return pureClass(...args); + } +} diff --git a/src/templates/css.js b/src/templates/css.js new file mode 100644 index 0000000..c1e2886 --- /dev/null +++ b/src/templates/css.js @@ -0,0 +1,4 @@ +module.exports = ` +.wrapper { +} +`; diff --git a/src/templates/exports.js b/src/templates/exports.js new file mode 100644 index 0000000..5d40bde --- /dev/null +++ b/src/templates/exports.js @@ -0,0 +1,8 @@ +const { stylingTypes } = require('../helpers'); + +module.exports = (style, componentName) => { + switch (style) { + default: + return `export default ${componentName};`; + } +} diff --git a/src/templates/functional.js b/src/templates/functional.js deleted file mode 100644 index 729118f..0000000 --- a/src/templates/functional.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; - - -const COMPONENT_NAME = () => { - return
; -}; - -export default COMPONENT_NAME; diff --git a/src/templates/pure-class.js b/src/templates/pure-class.js deleted file mode 100644 index a76ac87..0000000 --- a/src/templates/pure-class.js +++ /dev/null @@ -1,10 +0,0 @@ -import React, { PureComponent } from 'react'; - - -class COMPONENT_NAME extends PureComponent { - render() { - return
; - } -} - -export default COMPONENT_NAME; diff --git a/src/templates/react-imports.js b/src/templates/react-imports.js new file mode 100644 index 0000000..2651266 --- /dev/null +++ b/src/templates/react-imports.js @@ -0,0 +1,14 @@ +const { componentTypes } = require('../helpers'); + +module.exports = (componentType) => { + switch (componentType) { + case componentTypes.FUNCTIONAL: + return "import React from 'react';"; + + case componentTypes.CLASS: + return "import React, { Component } from 'react';"; + + case componentTypes.PURE_CLASS: + return "import React, { PureComponent } from 'react';"; + } +} diff --git a/src/templates/render-contents.js b/src/templates/render-contents.js new file mode 100644 index 0000000..8c74e1d --- /dev/null +++ b/src/templates/render-contents.js @@ -0,0 +1,17 @@ +const { stylingTypes } = require('../helpers'); + +module.exports = (style) => { + switch (style) { + case stylingTypes.STYLED: + return ''; + + case stylingTypes.CSS_MODULES: + return '
'; + + case stylingTypes.APHRODITE: + return '
'; + + default: + return '
'; + } +} diff --git a/src/templates/style-imports.js b/src/templates/style-imports.js new file mode 100644 index 0000000..40a3084 --- /dev/null +++ b/src/templates/style-imports.js @@ -0,0 +1,17 @@ +const { stylingTypes } = require('../helpers'); + +module.exports = (style, componentName) => { + switch (style) { + case stylingTypes.STYLED: + return "import styled from 'styled-components';"; + + case stylingTypes.CSS_MODULES: + return `import styles from './${componentName}.css';`; + + case stylingTypes.APHRODITE: + return "import { StyleSheet, css } from 'aphrodite';"; + + default: + return ''; + } +}