-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create component command #234
Open
robherba
wants to merge
9
commits into
2.x
Choose a base branch
from
create-component-command
base: 2.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
2eb0d41
feat: adding command to create components
robherba fca8121
feat: add a handler for the new command
robherba 27102ce
feat: add the new handler options
robherba 2f0fecd
feat: add functionality to create components
robherba 264aff6
feat: present a parent structure chooser if no directory is specified
robherba 182a6cb
feat: move and rename type to StructureHandlerResponse
robherba 772ce92
feat: refine documentation
robherba a36fb34
Merge branch 'develop' of github.com:emulsify-ds/emulsify-cli into cr…
robherba f8434ce
Merge branch '2.x' of github.com:emulsify-ds/emulsify-cli into create…
robherba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,104 @@ | ||
import log from '../lib/log'; | ||
import { | ||
EXIT_ERROR, | ||
EMULSIFY_SYSTEM_CONFIG_FILE, | ||
EMULSIFY_PROJECT_CONFIG_FILE, | ||
} from '../lib/constants'; | ||
import type { EmulsifySystem } from '@emulsify-cli/config'; | ||
import type { CreateComponentHandlerOptions } from '@emulsify-cli/handlers'; | ||
import getGitRepoNameFromUrl from '../util/getGitRepoNameFromUrl'; | ||
import getEmulsifyConfig from '../util/project/getEmulsifyConfig'; | ||
import getJsonFromCachedFile from '../util/cache/getJsonFromCachedFile'; | ||
import cloneIntoCache from '../util/cache/cloneIntoCache'; | ||
|
||
import generateComponent from '../util/project/generateComponent'; | ||
|
||
/** | ||
* Handler for the `component create` command. | ||
*/ | ||
export default async function componentCreate( | ||
name: string, | ||
{ directory }: CreateComponentHandlerOptions | ||
): Promise<void> { | ||
const emulsifyConfig = await getEmulsifyConfig(); | ||
if (!emulsifyConfig) { | ||
return log( | ||
'error', | ||
'No Emulsify project detected. You must run this command within an existing Emulsify project. For more information about creating Emulsify projects, run "emulsify init --help"', | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
// If there is no system or variant config, exit with a helpful message. | ||
if (!emulsifyConfig.system || !emulsifyConfig.variant) { | ||
return log( | ||
'error', | ||
'You must select and install a system before you can create components. To see a list of out-of-the-box systems, run "emulsify system list". You can install a system by running "emulsify system install [name]"', | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
// Parse the system name from the system repository path. | ||
const systemName = getGitRepoNameFromUrl(emulsifyConfig.system.repository); | ||
if (!systemName) { | ||
return log( | ||
'error', | ||
`The system specified in your project configuration is not valid. Please make sure your ${EMULSIFY_PROJECT_CONFIG_FILE} file contains a system.repository value that is a valid git url`, | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
// Make sure the given system is installed and has the correct branch/commit/tag checked out. | ||
try { | ||
await cloneIntoCache('systems', [systemName])(emulsifyConfig.system); | ||
} catch (e) { | ||
return log( | ||
'error', | ||
'The system specified in your project configuration is not clone-able, or has an invalid checkout value.', | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
// Load the system configuration file. | ||
const systemConf: EmulsifySystem | void = await getJsonFromCachedFile( | ||
'systems', | ||
[systemName], | ||
emulsifyConfig.system.checkout, | ||
EMULSIFY_SYSTEM_CONFIG_FILE | ||
); | ||
|
||
// If no systemConf is present, error with a helpful message. | ||
if (!systemConf) { | ||
return log( | ||
'error', | ||
`Unable to load configuration for the ${systemName} system. Please make sure the system is installed.`, | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
const variantName = emulsifyConfig.variant.platform; | ||
const variantConf = systemConf.variants?.find( | ||
({ platform }) => platform === variantName | ||
); | ||
|
||
if (!variantConf) { | ||
return log( | ||
'error', | ||
`Unable to find configuration for the variant ${variantName} within the system ${systemName}.`, | ||
EXIT_ERROR | ||
); | ||
} | ||
|
||
if (!name) { | ||
return log('error', 'Specify a name for the new component.'); | ||
} | ||
|
||
try { | ||
await generateComponent(variantConf, name, directory || ''); | ||
} catch (e) { | ||
log( | ||
'error', | ||
`Unable to create the ${name} component: ${(e as Error).toString()}` | ||
); | ||
} | ||
} |
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
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
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}` | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like a test written for this if possible. I can help if needed!