Hygen 처음 적용해보기

4 minute read
2024-07-15

세팅법

원래 기본적으로 맥에서는 패키지 매니저인 brew 를 사용하는데 hygen 이 없다고 나온다. 그래서 npm 으로 전역 설치했다.


hygen 전역 설치

npm i -g hygen


hygen 초기화

hygen init self


이 명령어를 호출하면 아래와 같이 내장 파일들이 설치된다.


image-20240715194012941


여기서 생성된 폴더명으로 명령어를 칠 수 있게 된다.


hygen generator {help/new/with-prompt} ...


나는 여기서 react 라는 템플릿 세트를 만들려고 한다. 그렇다면 아래와 같이 입력해준다.


hygen generator new react


나만의 템플릿 덩어리 만들기

그럼 react 라는 디렉토리가 새로 만들어진 걸 볼 수 있다. 거기에 원하는 자동 생성을 원하는 정적 파일들을 .ejs.t 확장자로 생성해줘야 한다.


image-20240715194409446


이렇게 정한 템플릿을 기준으로 name 옵션을 지정해서 동적인 파일을 생성할 수 있다.


hygen react new --name FooComponent


결과적으로 아래와 같이 생성된다.


image-20240715194519677


물론 템플릿 파일 상단에 meta 데이터를 꼭 빠뜨리면 안된다.


---
to: src/components/<%= h.changeCase.pascal(name) %>/style.ts
---


tilda 와 ejs 같이 사용하기

stories.ejs.t
---
to: "<%= `src/stories/ETC/${name}/${name}.stories.ts` %>"
---
 
import type { Meta, StoryObj } from '@storybook/react';
import <%= name %> from './<%= name %>';
 
const meta: Meta<typeof <%= name %>> = {
  title: 'ETC/<%= name %>',
  component: <%= name %>,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  // argTypes: {},
  // args: {},
} satisfies Meta<typeof <%= name %>>;
 
export default meta;
type Story = StoryObj<typeof <%= name %>>;
 
export const Base: Story = {
  // args: {},
};


혹은


<%= `${name}` %>


이렇게 사용할 수도 있다.


prompt

_templates/etc/story/prompt.cjs
function toPascalCase(str) {
  return str
    .split(/[\s_-]+/) // 공백, 밑줄, 대시로 문자열을 분할
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // 각 단어의 첫 글자는 대문자로, 나머지는 소문자로 변환
    .join(''); // 단어를 결합하여 최종 결과 반환
}
 
function isKebabCase(str) {
  return /^[a-z]+(-[a-z]+)*$/.test(str);
}
 
/**
 * @typedef {import('enquirer').} Enquirer
 */
 
/**
 * @param {object} options - The options for the prompt function.
 * @param {Enquirer} options.prompter - The prompter instance.
 * @param {any} options.args - The arguments for the prompt function.
 * @returns {Promise<void>}
 */
const prompt = async ({ prompter, args }) => {
  // 함수 본문
 
  const componentPromptResult = await prompter.prompt({
    type: 'input',
    name: 'name',
    message: '카테고리 컴포넌트 이름을 kebab-case 로 입력하세요.',
  });
 
  if (!componentPromptResult.name) {
    throw new Error('컴포넌트 이름을 입력하세요!');
  }
  if (!isKebabCase(componentPromptResult.name)) {
    throw new Error('컴포넌트 이름은 kebab-case 이어야 합니다.');
  }
 
  const meta = {
    name: toPascalCase(componentPromptResult.name),
    // files: multiSelectPromptResult.files, // 다른 값 넘길 때
    args,
  };
  console.log(meta);
 
  return meta;
};
 
// eslint-disable-next-line no-undef
module.exports = {
  prompt,
};
 


만약 여러 선택을 하려고 한다면 아래와 같이 prompt 추가할 수 있다.


prompt multiselect
const multiSelectPromptResult = await prompter.prompt({
  type: 'multiselect',
  name: 'files',
  message: '생성할 파일 목록을 선택해주세요.',
  initial: ['style', 'storybook'],
  choices: [
    {
      name: 'style',
      message: `${componentPromptResult.name}.style.ts`,
    },
    {
      name: 'storybook',
      message: `${componentPromptResult.name}.stories.tsx`,
    },
    {
      name: 'type',
      message: `${componentPromptResult.name}.type.ts`,
    },
  ],
});