-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add functionality to create components
- Loading branch information
Showing
1 changed file
with
145 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import * as fs from 'fs'; | ||
import { pathExists } from 'fs-extra'; | ||
import type { EmulsifyVariant } from '@emulsify-cli/config'; | ||
import { join, dirname } from 'path'; | ||
import { EMULSIFY_PROJECT_CONFIG_FILE } from '../../lib/constants'; | ||
import findFileInCurrentPath from '../fs/findFileInCurrentPath'; | ||
import log from '../../lib/log'; | ||
|
||
const storiesTemplate = ( | ||
componentName: string, | ||
filename: string, | ||
directory: string | ||
) => | ||
`import ${componentName}Twig from './${filename}.twig'; | ||
import ${componentName}Data from './${filename}.yml'; | ||
/** | ||
* Storybook Definition. | ||
*/ | ||
export default { title: '${directory[0].toUpperCase() + directory.slice(1)}/${ | ||
componentName[0].toUpperCase() + componentName.slice(1) | ||
}' }; | ||
export const ${componentName} = () => ${componentName}Twig(${componentName}Data); | ||
`; | ||
|
||
const twigTemplate = (filename: string, className: string) => | ||
`{# | ||
/** | ||
* Available variables: | ||
* - ${filename}__heading - the content of the heading (typically text) | ||
* - ${filename}__content - the content of the component (typically text) | ||
* | ||
* Available blocks: | ||
* - ${filename}__content - used to replace the content of the button with something other than text | ||
* for example: to insert an icon | ||
*/ | ||
#} | ||
{% set ${filename}__base_class = '${className}' %} | ||
<div className="${filename}"> | ||
{{ ${filename}__heading }} | ||
{% block ${filename}__content %} | ||
{# Component content goes here #} | ||
{{ ${filename}__content }} | ||
{% endblock %} | ||
</div> | ||
`; | ||
|
||
const scssTemplate = (className: string) => | ||
`.${className} { | ||
// Your SCSS code goes here | ||
} | ||
`; | ||
|
||
const ymlTemplate = (filename: string, componentName: string) => | ||
`${filename}__heading: '${componentName}' | ||
${filename}__content: 'It is a descriptive text of the ${componentName} component' | ||
`; | ||
|
||
/** | ||
* Installs a specified component within the Emulsify project the user is currently within. | ||
* | ||
* @param variant EmulsifyVariant object containing information about the component, where it lives, and how it should be created. | ||
* @param componentName string name of the component that should be created. | ||
* @param directory string name of the directory where it should be created. | ||
* @returns | ||
*/ | ||
export default async function generateComponent( | ||
variant: EmulsifyVariant, | ||
componentName: string, | ||
directory: string | ||
): Promise<void> { | ||
// Gather information about the current Emulsify project. If none exists, | ||
// throw an error. | ||
const path = findFileInCurrentPath(EMULSIFY_PROJECT_CONFIG_FILE); | ||
if (!path) { | ||
throw new Error( | ||
'Unable to find an Emulsify project to create the component into.' | ||
); | ||
} | ||
|
||
// Find the component's parent structure within the given variant configuration. If the | ||
// component's parent structure does not exist, throw an error. | ||
const structure = variant.structureImplementations.find( | ||
({ name }) => name === directory | ||
); | ||
if (!structure) { | ||
throw new Error( | ||
`The structure (${directory}) specified within the component ${componentName} is invalid.` | ||
); | ||
} | ||
|
||
// Calculate the destination path based on the path to the Emulsify project, the structure of the | ||
// component, and the component's name. | ||
const destination = join(dirname(path), structure.directory, componentName); | ||
|
||
// If the component already exists within the project, | ||
// throw an error. | ||
if (await pathExists(destination)) { | ||
throw new Error( | ||
`The component "${componentName}" already exists in ${structure.directory}` | ||
); | ||
} | ||
|
||
const filename = componentName | ||
.replace(/([a-z])([A-Z])/g, '$1_$2') | ||
.toLowerCase(); | ||
|
||
const className = componentName | ||
.replace(/([a-z])([A-Z])/g, '$1-$2') | ||
.toLowerCase(); | ||
|
||
// Create the component directory | ||
fs.mkdirSync(destination); | ||
|
||
// Generate twig template file | ||
const twigTemplateFile = twigTemplate(filename, className); | ||
const twigTemplatePath = join(destination, `${filename}.twig`); | ||
fs.writeFileSync(twigTemplatePath, twigTemplateFile); | ||
|
||
// Generate yml template file | ||
const ymlTemplateFile = ymlTemplate(filename, componentName); | ||
const ymlTemplatePath = join(destination, `${filename}.yml`); | ||
fs.writeFileSync(ymlTemplatePath, ymlTemplateFile); | ||
|
||
// Generate scss template file | ||
const scssTemplateFile = scssTemplate(className); | ||
const scssTemplatePath = join(destination, `${filename}.scss`); | ||
fs.writeFileSync(scssTemplatePath, scssTemplateFile); | ||
|
||
// Generate stories template file | ||
const storiesTemplateFile = storiesTemplate( | ||
componentName, | ||
filename, | ||
directory | ||
); | ||
const storiesTemplatePath = join(destination, `${filename}.stories.js`); | ||
fs.writeFileSync(storiesTemplatePath, storiesTemplateFile); | ||
|
||
return log( | ||
'success', | ||
`The ${componentName} component has been created in ${structure.directory}` | ||
); | ||
} |