LHJ

I'm a FE developer.

3월 - gulpfile.babel.js

01 Mar 2020 » node_gulp

첫 걸프세팅

NodeJS 공부하면서 처음 세팅해본 gulp 입니다.
여러가지 HTML Template를 사용하려고 시도했습니다.
아직 미완성입니다.
Jade(Pug), ejs, sass 공부가 충분치 않다는 걸 느꼈습니다.

      • <html lang="ko">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
            <title><%= title %></title>
            <style> body {
                    font-size: 12pt;
                    color: #006666;
                }
                h1 {
                    font-size: 18pt;
                    background-color: #AAFFFF;
                }
                pre {
                    background-color: #EEEEEE;
                }
            </style>
        </head>
        <body>
        <%
            var title = 'ejs'
         %>
        <h1><%= title %></h1>
        <p>Welcome to <%= title %>, 오잉 되네? 아싸 문법정리만 하자, 사용법만 확실하게 익히고 lastRun도 추가하고</p>
        <button class="<%= title %>" type="submit">전송</button>
        <input type="text" placeholder="<%= title + '연습' %>">
              
        EJS에서 변수는 &lt;%= %&gt;로 감쌉니다. <br>
        내부에 변수를 사용할 수도 있습니다. <br>
        자바스크립트 코드는 &lt;% %&gt; 안에 적어줍니다.
        </body>
        </html>
        
      • <html lang="ko">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
            <title><%= title %></title>
            <style> body {
                    font-size: 12pt;
                    color: #006666;
                }
                h1 {
                    font-size: 18pt;
                    background-color: #AAFFFF;
                }
                pre {
                    background-color: #EEEEEE;
                }
            </style>
        </head>
        <body>
        <%
            var title = 'ejs'
         %>
        <h1><%= title %></h1>
        <p>Welcome to <%= title %>, 오잉 되네? 아싸 문법정리만 하자, 사용법만 확실하게 익히고 lastRun도 추가하고</p>
        <button class="<%= title %>" type="submit">전송</button>
        <input type="text" placeholder="<%= title + '연습' %>">
              
        EJS에서 변수는 &lt;%= %&gt;로 감쌉니다. <br>
        내부에 변수를 사용할 수도 있습니다. <br>
        자바스크립트 코드는 &lt;% %&gt; 안에 적어줍니다.
        </body>
        </html>
        
      • <html lang="ko">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
            <title><%= title %></title>
            <style> body {
                    font-size: 12pt;
                    color: #006666;
                }
                h1 {
                    font-size: 18pt;
                    background-color: #AAFFFF;
                }
                pre {
                    background-color: #EEEEEE;
                }
            </style>
        </head>
        <body>
        <%
            var title = 'ejs'
         %>
        <h1><%= title %></h1>
        <p>Welcome to <%= title %>, 오잉 되네? 아싸 문법정리만 하자, 사용법만 확실하게 익히고 lastRun도 추가하고</p>
        <button class="<%= title %>" type="submit">전송</button>
        <input type="text" placeholder="<%= title + '연습' %>">
              
        EJS에서 변수는 &lt;%= %&gt;로 감쌉니다. <br>
        내부에 변수를 사용할 수도 있습니다. <br>
        자바스크립트 코드는 &lt;% %&gt; 안에 적어줍니다.
        </body>
        </html>
        
      • <html lang="ko">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
            <title><%= title %></title>
            <style> body {
                    font-size: 12pt;
                    color: #006666;
                }
              
                h1 {
                    font-size: 18pt;
                    background-color: #AAFFFF;
                }
              
                pre {
                    background-color: #EEEEEE;
                }
            </style>
        </head>
        <body>
        <%
            var title = 'ejs'
         %>
        <h1><%= title %></h1>
        <p>Welcome to <%= title %>, 오잉 되네? 아싸 문법정리만 하자, 사용법만 확실하게 익히고 lastRun도 추가하고</p>
        <button class="<%= title %>" type="submit">전송</button>
        <input type="text" placeholder="<%= title + '연습' %>">
              
        EJS에서 변수는 &lt;%= %&gt;로 감쌉니다. <br>
        내부에 변수를 사용할 수도 있습니다. <br>
        자바스크립트 코드는 &lt;% %&gt; 안에 적어줍니다.4444
        </body>
        </html>
        
      • /sp/
        • doctype html
          block lang
              - var language = 'ko'
          html(lang=language === 'ko' ? 'ko-KR' : language === 'en' ? 'en' : '')
              include _head
          body.dark-theme
              include _navigation
              | 안녕?
          
        • //- 현재 페이지 경로
          -var curPage='/'
                  
          //- 믹스인 2Depth 정의
          mixin navi2D(naviContents2)
              nav: ul
                  each D1_item in naviContents2
                      li
                          +naviLink(D1_item)
                          if D1_item.child
                              ul
                                  each D2_item in D1_item.child
                                      li
                                          +naviLink(D2_item)
                  
          mixin naviLink(item)
              a(href=item.link)=item.content+curPage
          
      • extends parts/_docTemplate
              
        block lang
            - var language = 'ko-KR'
              
        block pageTitle
            | 안녕하세요1
        
      • /libs/
        • // Scss 한줄 주석
          /* Scss 여러줄 주석, CSS로 컴파일 될 때 해석됩니다. */
          %btn {
            padding: 10px;
            border: 1px solid #222;
          }
                    
          .btn-check {
            @extend %btn;
            background-color: #cecece;
          }
                    
          .btn-click {
            @extend %btn;
            background-color: $fg-color;
          }
          
        • // 테마 변수 : light | dark
          $theme: dark;
                    
          // 조건 처리
          @if $theme == light {
            $bg-color: #f34e7b;
          } @else if $theme == very-light {
            $bg-color: #f8c9d6;
          } @else {
            $bg-color: #61051f;
          }
                    
          // 테마 클래스 속성 및
          // 배경/전경 컬러 색상 설정
          body.#{$theme}-theme {
            background-color: $bg-color;
            color: invert($bg-color);
          }
                    
          // if(조건, 참일 경우 값, 거짓일 경우 값)
          // 가운데 정렬 유무 설정
          $center-pos: false;
          $is-center: if($center-pos, auto, null);
                    
          .container {
            @include margin(null, $is-center);
          }
          
        • // 페이지 폭
          $page-width: 960px;
          // 컬럼 개수
          $columns: 24;
          // 거터
          $gutter: 20px;
          // 컬럼 폭
          $col-width: (($page-width - $gutter) - ($gutter*($columns - 1))) / $columns;
                    
          %col {
            float: left;
            margin-right: $gutter;
          }
                    
          // 조건이 3보다 작거나 같으면 코드 반복 생성
          @for $i from 1 through $columns {
            .col-#{$i} {
              @extend %col;
              width: ($col-width * $i) + ($gutter * ($i - 1));
            }
          }
                    
          // ---------------------------------------------
          /* 아이콘(icon) 설정 */
          // ---------------------------------------------
          @each $item, $w, $h in (git, 24px, 24px), (nodejs, 32px, 32px), (gulpjs, 24px, 24px), (jade, 16px, 16px) {
            .icon-#{$item} {
              width: $w;
              height: $h;
              background-image: url("images/#{$item},png");
            }
          }
                    
          // ---------------------------------------------
          /* 제목(Heading) 설정 */
          // ---------------------------------------------
          @each $H in (h1:3rem, h2:2rem, h3:1.7rem, h4:1.5rem, h5:1.3rem, h6:1rem) {
            #{nth($H, 1)} {
              font-size: nth($H, 2);
            }
          }
          
        • @mixin reset-list {
            padding-left: 0;
            list-style: none;
          }
                    
          @mixin html5-block {
            section, article, nav, aside, header, footer {
              display: block;
            }
          }
                    
          @mixin position($type:relative, $t:null, $l:null, $b:null, $r:null) {
            position: $type;
            top: $t;
            left: $l;
            bottom: $b;
            right: $r;
          }
                    
          @mixin size($w:null, $h:null) {
            width: $w;
            height: $h;
          }
                    
          @mixin transition($args...) {
            transition: $args;
          }
                    
          @mixin transform($args...) {
            transform: $args;
          }
                    
          @mixin margin($tb:null, $lr:null) {
            margin: {
              top: $tb;
              bottom: $tb;
              left: $lr;
              right: $lr;
            }
          }
          
        • _sprite.scss
        • // 폰트
          $ghothic: "Nanum Gothic", Dotum, Sans-serif;
          $myungjo: "Nanum Myungju", Gulim, serif;
          // 색상
          $fg-color: #382f1f;
          $bg-color: #fefcf5;
          
      • *, *::before, *::after {
          box-sizing: border-box;
        }
                
        html, body {
          height: 100%;
        }
                
        nav {
          background-color: #222;
        }
                
        #page {
          width: 100%;
          margin: 0 auto;
          padding: 30px;
                
          .article {
            position: relative;
            margin: {
              right: 10px;
              left: 30px;
            }
          }
                
          .container {
            padding-left: 5%;
            padding-right: 5%;
          }
                
          a {
            text-decoration: none;
            color: #069aea;
                
            &:hover {
              padding-bottom: 0.1rem;
              color: #023855;
            }
                
            .rgba & {
              color: rgba(6, 154, 234, 0.8);
            }
          }
        }
        
      • @import "modules/_sprite";
                
        /* 필요한 거 1배수만 가져오기 */
        .arrow-r {
          @include sprite($arrow-r);
        }
                
        /* 필요한 것만 1, 2배수 가져오기 */
        .arrow-r {
          @include retina-sprite($arrow-r-group);
        }
                
        /* 1배수 이미지 모두 */
        @include sprites($spritesheet-sprites);
                
        /* 1, 2배수 이미지 모두 */
        @include retina-sprites($retina-groups);
        
      • @charset "utf-8";
        @import "modules/_variables";
        @import "modules/_button";
        @import "pages/_page1";
        @import "modules/_mixins";
        @import "modules/_condition";
        @import "modules/_loop";
                
        @include html5-block;
                
        nav ul, #gbn ul, #lnb ul {
          @include reset-list;
          font-family: $myungjo;
          color: $bg-color;
        }
                
        .login-box {
          @include position(absolute, 0, 10px);
          @include size(24rem, 10rem);
        }
        /* 주석 */
        .error {
          @include position($type: absolute, $r: 10px);
          @include size(320px);
          @include transition(width 0.2s, height 0.4s ease-out);
          @include transform(scale(1.5));
        }
        
    •   <html lang="ko">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
            <title></title>
        </head>
        <body>
            
            
            
        </body>
        </html>
      
  • {
      "presets": [
        "@babel/preset-env"
      ]
    }
    
  • {
        "env": {
            "browser": true,
            "es6": true,
            "node": true
        },
        "extends": "eslint:recommended",
        "globals": {
            "Atomics": "readonly",
            "SharedArrayBuffer": "readonly"
        },
        "parserOptions": {
            "ecmaVersion": 2018,
            "sourceType": "module"
        },
        "rules": {
        }
    }
    
  • .idea/
    node_modules/
    dist/
    package-lock.json
    
  • {
      "port": 90,
      "livereload": true,
      "browser": "chrome",
      "path": {
        "del": {
          "dest": "dist/"
        },
        "browser": {
          "dest": "dist/"
        },
        "jade": {
          "src": "src/jade/**/!(_)*.jade",
          "parts": "src/jade/parts/*.jade",
          "dest": "dist/jade/"
        },
        "ejs": {
          "src": "src/ejs/**/!(_)*.{ejs,html}",
          "parts": "src/ejs/parts/*.ejs",
          "dest": "dist/ejs/"
        },
        "sass": {
          "src": "src/sass/**/*.{scss, sass}",
          "compassSrc": "src/sass/",
          "dest": "dist/css/"
        },
        "js": {
          "libs": "src/js/libs/**/*.js",
          "libsDest": "dist/js/libs/",
          "src": "src/js/*.js",
          "dest": "dist/js/",
          "filename": "DOMlibrary.js"
        },
        "img": {
          "src": "src/img/*",
          "dest": "dist/img/"
        },
        "spImg": {
          "src": "src/img/sp/*.png",
          "dest": "dist/img/sp/",
          "filename": "sprite.png",
          "imgPath": "../img/sp/sprite.png"
        },
        "retinaImg": {
          "src": "src/img/sp/*@2x.png",
          "filename": "sprite@2x.png",
          "retinaImgPath": "../img/sp/sprite@2x.png"
        }
      }
    }
    
  • "use strict";
      
    // 모듈 호출
    // gulp             : gulp 모듈
    // gulp-eslint      : javascript 문구 문법 검사
    // gulp-concat      : javascript 파일을 한 파일로 합쳐주는 모듈
    // gulp-uglify      : javascript 파일을 min 파일처럼 압축해주는 모듈
    // gulp-babel       : es6를 es5로 컴파일 해주는 모듈
    // gulp-rename      : 파일의 이름을 바꿔주는 모듈
    // gulp-jade        : HTML 템플릿 중 하나인 jade 템플릿을 사용할 수 있게 하는 모듈
    // gulp-ejs         : HTML 템플릿 중 하나인 ejs 템플릿을 사용할 수 있게 하는 모듈
    // gulp-sass        : sass를 사용할 수 있게 해주는 모듈
    // gulp-clean-css   : css 파일을 이쁘게 압축해주는 모듈
    // del              : 폴더(디렉터리)/파일 제거
    // gulp-connect     : local 서버와 연결하게 해주는 모듈
    // gulp-open        : 브라우저를 열게하는 모듈
    // gulp-sass-lint   : sass 문법검사 모듈
    // autoprefixer     : vend prefix를 자동으로 달아주는 모듈
    // gulp-postcss     : 위 autoprefixer와 같이 쓰는 모듈
    // gulp-imagemin    : image 파일 용량을 줄여주는 모듈
    // gulp.spritesmith : sprite 이미지와 CSS 자동 생성 모듈
    // vinyl-buffer     : gulp.spritesmith 모듈과 gulp-imagemin 모듈을 연결시켜주는 모듈
    // merge-stream     : stream을 merge시켜주는 모듈, 종료신호를 묶어서 보낼 수 있다.
      
    import fs from 'fs';
    import {src, dest, watch, series, parallel, lastRun} from 'gulp';
    import esLint from 'gulp-eslint';
    import jsConcat from 'gulp-concat';
    import jsUglify from 'gulp-uglify';
    import babel from 'gulp-babel';
    import rename from 'gulp-rename';
    import jade from 'gulp-jade';
    import ejs from 'gulp-ejs';
    import sass from 'gulp-sass';
    import cleanCss from 'gulp-clean-css';
    import del from 'del';
    import connect from 'gulp-connect';
    import open from 'gulp-open';
    import sassLint from 'gulp-sass-lint';
    import autoprefixer from 'autoprefixer';
    import postcss from 'gulp-postcss';
    import imagemin from 'gulp-imagemin';
    import spritesmith from 'gulp.spritesmith';
    import buffer from 'vinyl-buffer';
    import merge from 'merge-stream';
    import config from './config.json';
      
    // 테스트 중인 기능
    export const fsReadDir = () => {
        let fileList;
        let fileList2 = [];
        fileList = fs.readdirSync('./src/ejs/', {
            encoding: 'utf8',
            withFileTypes: true
        });
        fileList.forEach(function (file, index) {
            if (!file.isDirectory()) {
                fileList2.push(file.name)
            }
        });
        console.log(fileList);
        console.log(fileList[0].isDirectory());
        console.log(fileList[3].isDirectory());
        console.log(fileList2);
    };
      
      
    /*
     * For small tasks you can export arrow functions
     */
    // 파일 삭제
    export const Clean = () => {
        return del(
            [config.path.del.dest]
        );
    };
      
    // 이미지 용량 줄이기 및 sprite
    export const SpriteAndImgCompress = () => {
        const imgCompress = src(config.path.img.src)
            .pipe(imagemin([
                imagemin.gifsicle({interlaced: true}),
                imagemin.mozjpeg({quality: 80, progressive: true}),
                imagemin.optipng({optimizationLevel: 5}),
                imagemin.svgo({
                    plugins: [
                        {removeViewBox: true},
                        {cleanupIDs: false}
                    ]
                })
            ]))
            .pipe(dest(config.path.img.dest));
        const spriteData = src(config.path.spImg.src)
            .pipe(spritesmith({
                retinaSrcFilter: config.path.retinaImg.src,
                imgName: config.path.spImg.filename,
                retinaImgName: config.path.retinaImg.filename,
                imgPath: config.path.spImg.imgPath,
                retinaImgPath: config.path.retinaImg.retinaImgPath,
                cssName: '_sprite.scss',
                padding: 2
            }));
        const imgStream = spriteData.img
            .pipe(buffer())
            .pipe(imagemin())
            .pipe(dest(config.path.spImg.dest));
        const cssStream = spriteData.css
            .pipe(dest('src/sass/modules/'));
        return merge(imgCompress, imgStream, cssStream);
    };
      
    // Sass 컴파일
    // 주의할 점, Jade 템플릿과 어떻게 연동했냐에 따라서 watch 함수 내부를 수정해줄 필요가 있다.
    // Jade 템플릿에 style 태그에 인라인 형식으로 불러왔으면 watch 형식에서 Jade 탬플릿까지 수정을해줘야하는 함수를 호출해야한다.
    const Sass = () => {
        return src(config.path.sass.src, {sourcemaps: true})
            .pipe(sassLint({
                options: {
                    formatter: 'stylish'
                }
            }))
            .pipe(sassLint.format())
            .pipe(sass({
                sourceComments: false, // source 위치 주석
                outputStyle: 'compact' // nested, expanded, compact, compressed
            }).on('error', sass.logError))
            .pipe(postcss([autoprefixer()]))
            .pipe(cleanCss({format: 'keep-breaks'}))
            .pipe(dest(config.path.sass.dest, {sourcemaps: true}))
            .pipe(connect.reload())
    };
      
    // JS 문법 검사
    const HintJs = () => {
        return src(config.path.js.src)
            .pipe(esLint())
            .pipe(esLint.format())
    };
      
    // JS 병합
    const ConcatJs = () => {
        return src(config.path.js.src)
            .pipe(jsConcat(config.path.js.filename))
            .pipe(babel())
            .pipe(dest(config.path.js.dest));
    };
      
    // JS 압축
    const UglifyJs = () => {
        return src(config.path.js.dest + config.path.js.filename, {sourcemaps: true})
            .pipe(jsUglify())
            .pipe(rename({suffix: '.min'}))
            .pipe(dest(config.path.js.dest, {sourcemaps: true}))
            .pipe(connect.reload())
    };
      
    // JS libs 내보내기
    const Libs = () => {
        return src(config.path.js.libs)
            .pipe(dest(config.path.js.libsDest))
            .pipe(connect.reload())
    };
      
    // Jade 컴파일
    const JadeCompile = () => {
        return src(config.path.jade.src, {since: lastRun(JadeCompile)})
            .pipe(jade({
                pretty: true
            }))
            .pipe(dest(config.path.jade.dest))
            .pipe(connect.reload())
    };
      
    // ejs 컴파일
    const EjsCompile = () => {
        return src(config.path.ejs.src, {since: lastRun(EjsCompile)})
            .pipe(ejs({
                msg: "Hello Gulp!"
            }))
            .pipe(rename({
                extname: ".html"
            }))
            .pipe(dest(config.path.ejs.dest))
            .pipe(connect.reload())
    };
      
    // 웹 서버 업무 (LiveReload 사용)
    const Server = () => {
        return connect.server({
            root: config.path.browser.dest,
            port: config.port,
            livereload: config.livereload
        })
    };
      
    // 브라우저 오픈 업무
    const BrowserOpen = () => {
        const options = {
            uri: `http://localhost:${config.port}`,
            app: config.browser //chrome, firefox, iexplore, opera, safari
        };
        return src(config.path.browser.dest)
            .pipe(open(options)); // local 서버가 아닌 파일 경로로 열려면 '<%file.path%>' 를 넣어주면된다.
    };
      
    // 지속적 관찰(Watch) 업무 정의
    // Jade Template 내부 인라인 형식으로 들어가게 했을지도 모르므로 다음과 같이 모든 함수 뒤에 JadeCompile 함수를 호출하였다.
    const FileWatch = () => {
        watch([config.path.img.src, config.path.spImg.src], SpriteAndImgCompress);
        watch([config.path.jade.src, config.path.jade.parts], JadeCompile);
        watch([config.path.ejs.src, config.path.ejs.parts], EjsCompile);
        watch(config.path.sass.src, series(Sass, JadeCompile, EjsCompile));
        watch(config.path.js.src, series(HintJs, ConcatJs, UglifyJs, JadeCompile, EjsCompile));
        watch(config.path.js.libs, Libs, JadeCompile, EjsCompile);
    };
      
    /*
     * You could even use `export as` to rename exported tasks
     */
    exports.build = series(Clean, series(Libs, SpriteAndImgCompress), parallel(Sass, series(HintJs, ConcatJs, UglifyJs)), JadeCompile, EjsCompile);
      
    /*
     * You could even use `export as` to rename exported tasks
     */
    exports.watch = parallel(Server, BrowserOpen, FileWatch);
      
    /*
     * Export a default task
     */
    exports.default = series(Clean, series(Libs, SpriteAndImgCompress), parallel(Sass, series(HintJs, ConcatJs, UglifyJs)), JadeCompile, EjsCompile, parallel(Server, BrowserOpen, FileWatch));
    
  • {
      "name": "sass-training",
      "version": "1.0.0",
      "description": "",
      "main": "gulpfile.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "@babel/core": "^7.8.4",
        "@babel/preset-env": "^7.8.4",
        "@babel/register": "^7.8.3",
        "autoprefixer": "^9.7.4",
        "babel-cli": "^6.26.0",
        "babel-preset-env": "^1.7.0",
        "babel-register": "^6.26.0",
        "del": "^5.1.0",
        "ejs": "^3.0.1",
        "eslint": "^6.8.0",
        "gulp": "^4.0.2",
        "gulp-babel": "^8.0.0",
        "gulp-clean-css": "^4.2.0",
        "gulp-concat": "^2.6.1",
        "gulp-connect": "^5.7.0",
        "gulp-ejs": "^5.0.0",
        "gulp-eslint": "^6.0.0",
        "gulp-imagemin": "^7.1.0",
        "gulp-jade": "^1.1.0",
        "gulp-open": "^3.0.1",
        "gulp-postcss": "^8.0.0",
        "gulp-rename": "^2.0.0",
        "gulp-sass": "^4.0.2",
        "gulp-sass-lint": "^1.4.0",
        "gulp-uglify": "^3.0.2",
        "gulp.spritesmith": "^6.11.0",
        "merge-stream": "^2.0.0",
        "node-sass": "^4.13.1",
        "vinyl-buffer": "^1.0.1"
      },
      "browserslist": [
        "last 2 version",
        "> 2%",
        "IE 9",
        "IE 8"
      ]
    }