[javascript, Vue] fs을 이용한 Vue Component import 자동화 파일 생성
프런트엔드/javascript

[javascript, Vue] fs을 이용한 Vue Component import 자동화 파일 생성

<문제점>

컴포넌트들을 만든 후 index.js에 각 Vue 컴포넌트를 "수기로 import" 해야하는 상황이 발생하여 자동화가 필요하여 추가

자동화할 대상들은 components 하위에 있는 1depth Directory에 index.js 생성이 필요

하지만 각 1dpeth Directory 밑에는 모듈별 컴포넌트들이 존재하여 아래 Depth에 컴포넌트들도 모두 스캔이 필요 

 

<자동화 전 소스 예시>

/dialog/index.js

import ApprovalHistoryModal from './approval/ApprovalHistoryModal.vue';
import ApprovalLineModal from './approval/ApprovalLineModal.vue';
import ApprovalModal from './approval/ApprovalModal.vue';
import BasicAlert from './basic/BasicAlert.vue';
import BasicConfirm from './basic/BasicConfirm.vue';
import AddressSearchModal from './common/AddressSearchModal.vue';
import TypeContentModal from './common/TypeContentModal.vue';
import GroupModal from './group/GroupModal.vue';
import SingleSiteModal from './site/SingleSiteModal.vue';
import MultiUserModal from './user/MultiUserModal.vue';
import SingleUserModal from './user/SingleUserModal.vue';

export default {
  install(Vue) {
    Vue.component('ApprovalHistoryModal', ApprovalHistoryModal);
    Vue.component('ApprovalLineModal', ApprovalLineModal);
    Vue.component('ApprovalModal', ApprovalModal);
    Vue.component('BasicAlert', BasicAlert);
    Vue.component('BasicConfirm', BasicConfirm);
    Vue.component('AddressSearchModal', AddressSearchModal);
    Vue.component('TypeContentModal', TypeContentModal);
    Vue.component('GroupModal', GroupModal);
    Vue.component('SingleSiteModal', SingleSiteModal);
    Vue.component('MultiUserModal', MultiUserModal);
    Vue.component('SingleUserModal', SingleUserModal);
  },
};

 

/components/export.js

export { default as dialog } from './dialog';
export { default as file } from './file';
export { default as input } from './input';
export { default as table } from './table';
export { default as tree } from './tree';

 

 /components/index.js

import * as components from './export';

export default function install(Vue) {
  Object.values(components).forEach((component) => {
    Vue.use(component);
  });
}

src/index.js

import component from './common/components';

export default {
  install(Vue) {
    Vue.use(component);
  },
};

<작업 내용>

/components/index.js는 수기로 작성할 일이 없어 자동화 대상에서 제외

공통 컴포넌트 1Depth Directory (/dialog/index.js, /file/index.js, 등) 와 최상위 디렉토리(export.js)를 대상으로 처리

 

공통 컴포넌트  경로는 './src/common/components' 으로 지정하고 처리

[ /script/fileGenerate.js ]

const fs = require('fs');
const rootFolder = './src/common/components';

const readDir = (path, filePaths = []) => {
  const files = fs.readdirSync(path);
  files.forEach((fileName) => {
    const name = `${path}/${fileName}`;
    if (fs.statSync(name).isDirectory()) {
      return readDir(name, filePaths);
    } else {
      filePaths.push(name);
    }
  });

  return filePaths;
};

const groupingDir = (files, moduleMap = {}) => {
  files.forEach((file) => {
    const filePathAndName = file.replace(rootFolder, '').substring(1);

    if (filePathAndName.includes('/')) {
      const dirName = filePathAndName.split('/')[0];
      moduleMap[dirName] = [
        ...(moduleMap[dirName] || []),
        ...[`.${filePathAndName.replace(dirName, '')}`],
      ];
    }
  });

  return moduleMap;
};

const writeIndexFile = (modulePathMap) => {
  for (const [key, value] of Object.entries(modulePathMap)) {
    const rootPath = `${rootFolder}/${key}/index.js`;
    let content = '';

    //import
    value.forEach((modulePath) => {
      // index.js인 경우 패스
      if (modulePath.includes('index.js')) return true;

      const moduleName = modulePath.substring(
        modulePath.lastIndexOf('/') + 1,
        modulePath.lastIndexOf('.'),
      );
      content += `import ${moduleName} from '${modulePath}';\n`;
    });

    content += '\nexport default {\n' + '  install(Vue) {\n';
    // Vue.component
    value.forEach((modulePath) => {
      // index.js인 경우 패스
      if (modulePath.includes('index.js')) return true;

      const moduleName = modulePath.substring(
        modulePath.lastIndexOf('/') + 1,
        modulePath.lastIndexOf('.'),
      );
      content += `    Vue.component('${moduleName}', ${moduleName});\n`;
    });
    content += '  },\n' + '};';

    fs.writeFileSync(rootPath, content);
  }
};

const writeExportFile = (modulePathMap) => {
  const rootPath = `${rootFolder}/export.js`;
  let content = '';
  for (const [key] of Object.entries(modulePathMap)) {
    content += `export { default as ${key} } from './${key}';\n`;
  }

  fs.writeFileSync(rootPath, content);
};

const modulePathMap = groupingDir(readDir(rootFolder));
writeIndexFile(modulePathMap);
writeExportFile(modulePathMap);
  • readDir() 함수를 통해 지정한 rootFolder에 하위 파일들을 모두 스캔 후 배열(filePaths) 에 저장
    • Directory인 경우는 재귀호출을 하여 안에 있는 파일들을 조회
  • groupingDir()을 통해 읽어드린 배열을 최상위 Directory 명으로 오브젝트를 생성
{
	dialog: [파일path1, 파일path2],
	input: [파일path1, 파일path2],
	...
}
  • writeIndexFile()을 통해 1depth Directory 밑에 index.js를 생성
    • index.js에는 각 컴포넌트 모듈들을 import 및 Vue.component 등록 로직을 추가
  • writeExportFile()을 통해 최상위 경로에 export.js를 생성
    • export.js에는 1depth Directory에 있는 index.js를 등록하는 로직을 추가

 

[ /package.json

npm run serve 또는 npm run build:lib라는 명령어가 들어오는 경우

"node script/fileGenerate.js" 문구를 추가하여 fileGenerate.js가 실행되도록 추가

{
  "name": "컴포넌트명",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "node script/fileGenerate.js && vue-cli-service serve",
    "build:lib": "node script/fileGenerate.js && vue-cli-service build --target lib --name micro-portal-component ./src/index.js",
  }
  ...
}

 

 

'프런트엔드 > javascript' 카테고리의 다른 글

[javascript] axios multi form 데이터 처리  (0) 2023.10.12