-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from jakala-na/feat/component-generate-cli
feat(component-generate-cli): implements new component scafollding with CLI
- Loading branch information
Showing
11 changed files
with
624 additions
and
30 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,38 @@ | ||
import chalk from 'chalk'; | ||
import inquirer from 'inquirer'; | ||
|
||
import prompts from './prompts.mjs'; | ||
import { fetchCTFContentTypes, scaffoldComponentFiles } from './utils.mjs'; | ||
|
||
if (!process.env.CONTENTFUL_SPACE || !process.env.CONTENTFUL_ENVIRONMENT || !process.env.CONTENTFUL_DELIVERY_API) { | ||
console.log( | ||
chalk.whiteBright.bgRed.bold( | ||
"You'll need to provide CONTENTFUL_SPACE, CONTENTFUL_ENVIRONMENT and CONTENTFUL_DELIVERY_API in your .env.local file for the CLI to be functional." | ||
) | ||
); | ||
process.exit(); | ||
} | ||
|
||
const contentTypesList = await fetchCTFContentTypes( | ||
process.env.CONTENTFUL_SPACE, | ||
process.env.CONTENTFUL_ENVIRONMENT, | ||
process.env.CONTENTFUL_DELIVERY_API, | ||
prompts.promptContentType.choices | ||
); | ||
|
||
inquirer | ||
.prompt([ | ||
{ | ||
...prompts.promptContentType, | ||
choices: contentTypesList, | ||
}, | ||
]) | ||
.then(({ promptContentType: contentType }) => { | ||
if (contentType === prompts.promptContentType.choices[0]) { | ||
inquirer.prompt([prompts.promptComponentName]).then(({ promptComponentName: componentName }) => { | ||
scaffoldComponentFiles(componentName, false); | ||
}); | ||
} else { | ||
scaffoldComponentFiles(contentType, true); | ||
} | ||
}); |
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,22 @@ | ||
const prompts = { | ||
promptContentType: { | ||
name: 'promptContentType', | ||
type: 'list', | ||
message: 'Please choose a CTF content type to generate the component for:', | ||
choices: ['---No content type yet---'], | ||
}, | ||
promptComponentName: { | ||
name: 'promptComponentName', | ||
type: 'input', | ||
message: 'Please input the component name (use kebab case):', | ||
validate: (input) => { | ||
if (!input || input === '') { | ||
return 'Please provide a component name.'; | ||
} else { | ||
return true; | ||
} | ||
}, | ||
}, | ||
}; | ||
|
||
export default prompts; |
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 @@ | ||
export { {{pascalCase name}} } from './{{hyphenCase name}}'; |
17 changes: 17 additions & 0 deletions
17
cli/scaffolds/components/{{hyphenCase name}}-client.{{ext}}
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,17 @@ | ||
'use client'; | ||
|
||
import { ResultOf } from 'gql.tada'; | ||
|
||
import { Component{{pascalCase name}}FieldsFragment } from '#/components/{{hyphenCase name}}/{{hyphenCase name}}'; | ||
import { {{pascalCase name}} } from '#/components/ui/{{hyphenCase name}}'; | ||
|
||
import { useComponentPreview } from '../hooks/use-component-preview'; | ||
|
||
export const {{pascalCase name}}Client: React.FC<{ | ||
data: ResultOf<typeof Component{{pascalCase name}}FieldsFragment>; | ||
}> = (props) => { | ||
const { data: originalData } = props; | ||
const { data, addAttributes } = useComponentPreview<typeof originalData>(originalData); | ||
|
||
return <{{pascalCase name}} id={data.sys.id} addAttributes={addAttributes} />; | ||
}; |
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,21 @@ | ||
import { FragmentOf, graphql, readFragment } from 'gql.tada'; | ||
|
||
import { {{pascalCase name}}Client } from './{{hyphenCase name}}-client'; | ||
|
||
export const Component{{pascalCase name}}FieldsFragment = graphql(` | ||
fragment Component{{pascalCase name}} on {{pascalCase name}} { | ||
__typename | ||
sys { | ||
id | ||
} | ||
} | ||
`); | ||
|
||
export type {{pascalCase name}}Props = { | ||
data: FragmentOf<typeof Component{{pascalCase name}}FieldsFragment>; | ||
}; | ||
|
||
export const {{pascalCase name}}: React.FC<{{pascalCase name}}Props> = (props) => { | ||
const data = readFragment(Component{{pascalCase name}}FieldsFragment, props.data); | ||
return <{{pascalCase name}}Client data={data} />; | ||
}; |
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 @@ | ||
export * from './{{hyphenCase name}}'; |
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,29 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { {{pascalCase name}} } from './{{hyphenCase name}}'; | ||
|
||
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export | ||
const meta = { | ||
title: 'UI/{{startCase name}}', | ||
component: {{pascalCase name}}, | ||
parameters: { | ||
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout | ||
layout: 'centered', | ||
}, | ||
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs | ||
tags: ['autodocs'], | ||
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes | ||
argTypes: { | ||
id: {}, | ||
}, | ||
} satisfies Meta<typeof {{pascalCase name}}>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args | ||
export const Default: Story = { | ||
args: { | ||
id: '{{ snakeCase name }}_id', | ||
}, | ||
}; |
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,13 @@ | ||
export interface {{pascalCase name}}Props { | ||
id: string; | ||
addAttributes?: (name: string) => object | null; | ||
} | ||
|
||
export const {{pascalCase name}} = ({ id, addAttributes = () => ({}) }: {{pascalCase name}}Props) => { | ||
return ( | ||
<div {...addAttributes('attributes')}> | ||
<div>{{pascalCase name}}</div> | ||
<div>{id}</div> | ||
</div> | ||
); | ||
}; |
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,95 @@ | ||
import * as fs from 'node:fs'; | ||
import path from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
|
||
import chalk from 'chalk'; | ||
import * as contentful from 'contentful'; | ||
import ora from 'ora'; | ||
import { Scaffold } from 'simple-scaffold'; | ||
|
||
export const pascalToHyphen = (str) => { | ||
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | ||
}; | ||
|
||
function hyphenToPascal(str) { | ||
return str | ||
.split('-') | ||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) | ||
.join(''); | ||
} | ||
|
||
/* | ||
Fetches all available CTF content types and returns an array with formatted IDs. | ||
*/ | ||
export const fetchCTFContentTypes = async (spaceID, envID, token, contentTypesPlaceholder) => { | ||
const ctfClient = contentful.createClient({ | ||
space: spaceID, | ||
environment: envID, | ||
accessToken: token, | ||
}); | ||
const contentTypes = [...contentTypesPlaceholder]; | ||
|
||
const spinner = ora(`Fetching content types for space ${spaceID}...`).start(); | ||
|
||
await ctfClient | ||
.getContentTypes() | ||
.then((response) => { | ||
response.items.forEach((item) => { | ||
contentTypes.push(pascalToHyphen(item.sys.id)); | ||
}); | ||
spinner.succeed(chalk.green('Done!')); | ||
}) | ||
.catch(() => { | ||
console.error(); | ||
spinner.fail('Not able to fetch the content types.'); | ||
}); | ||
|
||
return contentTypes; | ||
}; | ||
|
||
/* | ||
Generates component files using content type or custom input string. | ||
Updates mappings.ts with the new component. | ||
*/ | ||
export const scaffoldComponentFiles = (contentType, updateMappings) => { | ||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = path.dirname(__filename); | ||
|
||
// Scaffold component files. | ||
const config = { | ||
name: contentType, | ||
data: { | ||
ext: 'tsx' | ||
}, | ||
templates: [path.join(__dirname, 'scaffolds', 'components'), path.join(__dirname, 'scaffolds', 'ui')], | ||
createSubFolder: true, | ||
output: (fullPath, baseDir, baseName) => { | ||
let outputPath = '../components'; | ||
|
||
if (baseDir.includes('scaffolds/ui')) { | ||
outputPath = '../components/ui'; | ||
} | ||
|
||
return path.join(__dirname, outputPath, contentType); | ||
}, | ||
}; | ||
|
||
const scaffold = Scaffold(config); | ||
|
||
// Update mappings.ts with our new component after a successful scaffold. | ||
if (updateMappings) { | ||
scaffold | ||
.then(() => { | ||
const mappingFilePath = path.join(__dirname, '../components/component-renderer/mappings.ts'); | ||
const mappings = fs.readFileSync(mappingFilePath, 'utf-8').split('\n'); | ||
mappings.splice( | ||
-2, | ||
0, | ||
` ${hyphenToPascal(contentType)}: dynamic(() => import(\'#/components/${contentType}\').then((mod) => mod.${hyphenToPascal(contentType)})),` | ||
); | ||
const updatedMappings = mappings.join('\n'); | ||
fs.writeFileSync(mappingFilePath, updatedMappings, { encoding: 'utf-8' }); | ||
}) | ||
.catch(() => console.error); | ||
} | ||
}; |
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
Oops, something went wrong.