vite type generator 플러그인 만들기

nevertheless my pace·2023년 3월 15일
0

목적 : openAPi type generator를 npm 스크립트에 포함하여 사용하고 있었는데
route path , modal Name , 이미지 파일명 의 타입들도 자동으로 생성하면 좋을것같아서 스크립트를 만들었지만 매번 generater가 추가 될 때 마다 스크립트에 추가함이 번거롭고 package.json이 길어지는 문제가 발생

vite plugin으로 특정 디렉토리가 변경될때마다 자동으로 스크립트가 수행되는
플러그인을 만들기로함.

vite.config.ts

import runScript from './plugin';
import modalType from './plugin/modalType';
import routeType from './plugin/routeType';
import openapi from './plugin/openapi';
import imageType from './plugin/imageType';

export default defineConfig({
  plugins: [
    react(),
    checker({
      typescript: {
        tsconfigPath: 'tsconfig.json',
      },
      eslint: {
        lintCommand: 'eslint "./src/**/*.{ts,tsx}"', 
        dev: {
          logLevel: ['error'],
        },
      },
    }),
    dynamicImport(),
    runScript([modalType, routeType, openapi, imageType]),
  ],
});
  1. runScript() 만들기

실행 조건 : 빌드시,dev서버 실행시, 각 스크립트들이 감지하고있는 파일이 변경되었을때

import { Plugin } from 'vite';
import { ViteMode, isServeMode, isBuildMode } from './utils/viteModes';

export type RunScriptOption = {
  name: string;
  run: (env?: RunOption) => Promise<void>;
  matcher: (path: string) => boolean;
};
export type RunOption = {
  env: EnvType;
};
type EnvType = Record<string, unknown>;

export default function runScript(options: RunScriptOption[]): Plugin {
  let viteMode: ViteMode;
  const enableWatcher = true;
  let env: Record<string, unknown>;
  const runAll = async (env: EnvType, path?: string) => {
    return Promise.all(
      options.map(async (option) => {
        if (path === undefined || option.matcher(path)) {
          const result = await option.run({ env });

          return result;
        }
        return null;
      }),
    );
  };

  return {
    name: 'run-scripts',
    async config(_config, env) {
      viteMode = env.command;
    },
    async configResolved(_config) {
      env = _config.env;
    },
    async buildStart() {
      const isServe = isServeMode(viteMode);
      const isBuild = isBuildMode(viteMode);
      try {
        if (isServe) await runAll(env);
        if (isBuild) await runAll(env);
      } catch (error) {
        if (isBuild) throw error;
      }
    },
    configureServer(server) {
      if (!enableWatcher) return;

      const listener = async (filePath = '') => {
        await runAll(env, filePath);
      };

      server.watcher.on('add', listener);
    },
  };
}
  1. 필요한 타입 generator 스크립트 구현
    각 스크립트 들은 아래와 같은 타입을 리턴하여야한다.
    run : .ts file을 생성하는 스크립트
    matcher : run을 실행할 조건(boolean)
export type RunScriptOption = {
  name: string;
  run: (env?: RunOption) => Promise<void>;
  matcher: (path: string) => boolean;
};
  1. 이제 폴더를 탐색하여 폴더명 type이 정의하는 스크립트를 만들어보자

모달컴포넌트의 경우 /src/components/modals 디렉토리에 모여 있다

/** 모달 Name Type Generator*/
import fs from 'node:fs';
import writeFile from '../utils/writeFile';
import { RunScriptOption } from '..';

const run = () => {
  try {
    const result = fs
      .readdirSync('./src/components/modals')
      .filter((fileName) => fileName.endsWith('.tsx'))
      .map((fileName) => `'${fileName.replace('.tsx', '')}'`);

    // debugLog(result);

    writeFile('./src/components/modals/_generated.types.ts', `export type ModalName = ${result.join('|')};`);
  } catch (error) {
    console.log(error);
  }
};

const matcher = (path: string) => {
  return path.indexOf('/components/modals') !== -1 && path.indexOf('.tsx') !== -1;
};

export default {
  name: 'Generate Modal Type',
  run,
  matcher,
} as RunScriptOption;
  1. 자동생성된 타입파일
export type ModalName = 'NoShowModal' | 'SetAmountModal' | 'SetExchangeRateModal' | 'SetPaymentDateModal';

0개의 댓글