나만의 프로젝트 템플릿 생성기 만들기

컴퓨터 어디에서나 사용자 정의 CLI 프로젝트 템플릿 생성기를 구축하십시오. 이 문서How to build your own project templates using Node CLI and typescript의 지침을 사용했지만 몇 가지 문제가 발생하여 직접 문제를 해결하고 새 문서를 작성하기로 결정했습니다.

나만의 템플릿 생성기를 만들고 싶은 이유는 무엇입니까?



때때로 새 프로젝트를 만들고 싶지만 이전에 설정한 것을 기반으로 합니다. React with Node가 필요하지만 이전 프로젝트에서 모든 것을 구현한 방법을 잊어버렸기 때문에 이전 프로젝트를 통해 소싱하는 대신 템플릿을 만들어 이 템플릿 생성기에서 사용하는 것이 어떻습니까?

건너뛰고 직접 구축하지 않으려면 여기에서 Project Template Generator에 대한 저장소를 참조하십시오.

시작하자



1. 새 TypeScript 프로젝트 만들기



1. create a new project folder (mkdir PROJECT_NAME & cd PROJECT NAME)
2. run npm init (to initialize a new node project)
3. run npm add -D typescript ts-node nodemon
- ts-node is used to run typescript without compiling
- nodemon is used to run/restart node automatically when files changed
4. run npx tsc --init
5. adjust tsconfig.json to the following
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "declaration": true,
    "sourceMap": true,
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
  }
}
6. create folder src and index.ts file

2. 기본 템플릿 생성기 프로젝트에 sample-project 추가



1. create a the following folder tree inside your project: src/templates/sample-project.
2. inside the sample-projects folder create a package.json file that contains the following:
{
  "name": "sample-project",
  "version": "1.0.0",
  "license": "MIT"
}

이제 구조는 다음과 같아야 합니다.



3. "src/index.ts" 상단에 다음을 추가합니다.



1. #!/usr/bin/env node
   This is known as "shebang" this tells node to run the typescript code.

4. 이제 CLI에서 템플릿 폴더를 선택 항목으로 읽을 수 있습니다.



1. run npm add -D @types/node @types/inquirer
2. run npm add inquirer
3. update src/index.ts

   import * as fs from 'fs';
   import * as path from 'path';
   import * as inquirer from 'inquirer';
   import chalk from 'chalk';

   const CHOICES = fs.readdirSync(path.join(__dirname, 'templates'));
   const QUESTIONS = [
   {
      name: 'template',
      type: 'list',
      message: 'What project template would you like to use?',
      choices: CHOICES
   },
   {
      name: 'name',
      type: 'input',
      message: 'New project name?'
   }];

   inquirer.prompt(QUESTIONS)
   .then(answers => {
      console.log(answers);
   });

4. to test update package.json script
   "scripts": {
      "start": "ts-node src/index.ts"
   }
   and run npm start



src/templates에 자신의 템플릿을 추가하면 목록에 더 많은 템플릿이 있습니다.

5. 입력 옵션 사용



1. update src/index.ts with the following 
2. export interface CliOptions {
    projectName: string
    templateName: string
    templatePath: string
    tartgetPath: string
   }
   const CURR_DIR = process.cwd();
   inquirer.prompt(QUESTIONS)
     .then(answers => {
      const projectChoice = answers['template'];
      const projectName = answers['name'];
      const templatePath = path.join(__dirname, 'templates', projectChoice);
      const tartgetPath = path.join(CURR_DIR, projectName);
      const options: CliOptions = {
          projectName,
          templateName: projectChoice,
          templatePath,
          tartgetPath
      }
      console.log(options);
   });

6. 프로젝트 폴더 생성



질문에서 새 프로젝트 이름? 루트 디렉터리에 새 폴더 "projectName"을 만듭니다.

function createProject(projectPath: string) {
    if (fs.existsSync(projectPath)) {
        console.log(chalk.red(`Folder ${projectPath} exists. Delete or use another name.`));
        return false;
    }
    fs.mkdirSync(projectPath);

    return true;
}

실패시 기능 정지

inquirer.prompt(QUESTIONS)
.then(answers => {
    ....

    if (!createProject(tartgetPath)) {
        return;
    }
});

7. 선택한 템플릿에서 새 프로젝트로 파일 및 폴더 복사



1. Add to src/index.ts
2. // list of file/folder that should not be copied
   const SKIP_FILES = ['node_modules', '.template.json'];
   function createDirectoryContents(templatePath: string, projectName: string) {
    // read all files/folders (1 level) from template folder
    const filesToCreate = fs.readdirSync(templatePath);
    // loop each file/folder
    filesToCreate.forEach(file => {
        const origFilePath = path.join(templatePath, file);

        // get stats about the current file
        const stats = fs.statSync(origFilePath);

        // skip files that should not be copied
        if (SKIP_FILES.indexOf(file) > -1) return;

        if (stats.isFile()) {
            // read file content and transform it using template engine
            let contents = fs.readFileSync(origFilePath, 'utf8');
            // write file to destination folder
            const writePath = path.join(CURR_DIR, projectName, file);
            fs.writeFileSync(writePath, contents, 'utf8');
        } else if (stats.isDirectory()) {
            // create folder in destination folder
            fs.mkdirSync(path.join(CURR_DIR, projectName, file));
            // copy files/folder inside current folder recursively
            createDirectoryContents(path.join(templatePath, file), 
            path.join(projectName, file));
        }
    });
   }

템플릿을 만든 후 다음 코드를 추가합니다.

....
if (!createProject(tartgetPath)) {
    return;
}
createDirectoryContents(templatePath, projectName);
....

8. 프로그램을 CLI로 테스트하기



스크립트 작성을 위한 도구 "shx"설치

1. Run npm add -D shx
2. Add the following build script to package.json 
   "build": "tsc && shx rm -rf dist/templates && shx cp -r src/templates dist"
3. npm run build
4. Add bin to package.json
   "bin": {
      "template-generator": "./dist/index.js"
   }
5. Register "template-generator" as a command line interface
   run npm link

성공하면 시스템의 어느 곳에서나 "템플릿 생성기"명령을 실행할 수 있습니다. (파일 읽기/쓰기 권한이 있는지 확인하십시오.)

9. 마지막 단계: 입력으로 생성된 새 프로젝트로 프로젝트 이름 바꾸기



이제 주어진 질문 목록에서 템플릿을 선택한 다음 새 프로젝트 이름을 입력할 수 있지만 복사되는 템플릿 파일은 새 package.json의 프로젝트 이름과 정확히 동일하며 이를 자동화하려고 합니다.

1. update template "src/templates/sample-project/package.json" with a placholder name
   {
      "name": "<%= projectName %>",
      "version": "1.0.0",
      ....
   }
2. npm add ejs
   add -D @types/ejs
3. update src/utils/template.ts to render template under utils
   import * as ejs from 'ejs';
   export interface TemplateData {
      projectName: string
   }
   export function render(content: string, data: TemplateData) {
      return ejs.render(content, data);
   }
4. Add code to transform the content inside "src/index.ts" function "createDirectoryContents"
   if (stats.isFile()) {
      // read file content and transform it using template engine
      let contents = fs.readFileSync(origFilePath, 'utf8');
      contents = template.render(contents, { projectName });
   }
5. run npm build and then generate-template to test that the new project name is inserted in the "<%= projectName %>" placeholder.

이제 프로젝트 템플릿 생성기가 완료되었습니다.

여기에 뭔가 놓친 경우를 대비하여 전체 src/index.ts 파일이 있습니다.

#!/usr/bin/env node

import * as fs from 'fs';
import * as path from 'path';
import * as inquirer from 'inquirer';
import chalk from 'chalk';
import * as template from './utils/template';
import * as shell from 'shelljs';

const CHOICES = fs.readdirSync(path.join(__dirname, 'templates'));
const QUESTIONS = [
{
    name: 'template',
    type: 'list',
    message: 'What template would you like to use?',
    choices: CHOICES
},
{
    name: 'name',
    type: 'input',
    message: 'Please input a new project name:'
}];

export interface CliOptions {
    projectName: string
    templateName: string
    templatePath: string
    tartgetPath: string
}

const CURR_DIR = process.cwd();

inquirer.prompt(QUESTIONS).then(answers => {
    const projectChoice = answers['template'];
    const projectName = answers['name'];
    //@ts-ignore
    const templatePath = path.join(__dirname, 'templates', projectChoice);
    //@ts-ignore
    const tartgetPath = path.join(CURR_DIR, projectName);

    const options: CliOptions = {
        //@ts-ignore
        projectName,
        //@ts-ignore
        templateName: projectChoice,
        templatePath,
        tartgetPath
    }

    if (!createProject(tartgetPath)) {
        return;
    }

    //@ts-ignore
    createDirectoryContents(templatePath, projectName);

    postProcess(options);
});

function createProject(projectPath: string) {
    if (fs.existsSync(projectPath)) {
        console.log(chalk.red(`Folder ${projectPath} exists. Delete or use another name.`));
        return false;
    }
    fs.mkdirSync(projectPath);

    return true;
}

const SKIP_FILES = ['node_modules', '.template.json'];

function createDirectoryContents(templatePath: string, projectName: string) {
    // read all files/folders (1 level) from template folder
    const filesToCreate = fs.readdirSync(templatePath);
    // loop each file/folder
    filesToCreate.forEach(file => {
        const origFilePath = path.join(templatePath, file);

        // get stats about the current file
        const stats = fs.statSync(origFilePath);

        // skip files that should not be copied
        if (SKIP_FILES.indexOf(file) > -1) return;

        if (stats.isFile()) {
            // read file content and transform it using template engine
            let contents = fs.readFileSync(origFilePath, 'utf8');
            contents = template.render(contents, { projectName });
            // write file to destination folder
            const writePath = path.join(CURR_DIR, projectName, file);
            fs.writeFileSync(writePath, contents, 'utf8');
        } else if (stats.isDirectory()) {
            // create folder in destination folder
            fs.mkdirSync(path.join(CURR_DIR, projectName, file));
            // copy files/folder inside current folder recursively
            createDirectoryContents(path.join(templatePath, file), path.join(projectName, file));
        }
    });
}

function postProcess(options: CliOptions) {
    const isNode = fs.existsSync(path.join(options.templatePath, 'package.json'));
    if (isNode) {
        shell.cd(options.tartgetPath);
        const result = shell.exec('npm install');
        if (result.code !== 0) {
            return false;
        }
    }

    return true;
}


및 사용할 전체 프로젝트에 대한 링크: Project Template Generator

좋은 웹페이지 즐겨찾기