목적 : 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]),
],
});
실행 조건 : 빌드시,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);
},
};
}
export type RunScriptOption = {
name: string;
run: (env?: RunOption) => Promise<void>;
matcher: (path: string) => boolean;
};
모달컴포넌트의 경우 /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;
export type ModalName = 'NoShowModal' | 'SetAmountModal' | 'SetExchangeRateModal' | 'SetPaymentDateModal';