LHJ

I'm a FE developer.

5월 - gulpfile.babel.js - module

17 May 2020 » node_gulp
  • repository url

      • import del from 'del';
        import config from '../../config.json';
              
        export const clean_dist = () => {
            return del([
                config.dist,
                `${config.src}/scss/libs/*`,
                `!${config.src}/scss/libs/${config.libs.scss}`,
                `${config.src}/js/libs/*`,
                `!${config.src}/js/libs/${config.libs.js}`
            ])
        };
              
        export const clean_css = () => {
            return del([
                `${config.dist}/css`,
                `${config.src}/scss/libs/*`,
                `!${config.src}/scss/libs/${config.libs.scss}`
            ])
        };
              
        export const clean_fonts = () => {
            return del(`${config.dist}/fonts`)
        }
              
        export const clean_js = () => {
            return del([
                `${config.dist}/js`,
                `${config.src}/js/libs/*`,
                `!${config.src}/js/libs/${config.libs.js}`
            ])
        };
              
        export const clean_html = () => {
            return del(`${config.dist}/html`)
        };
              
        export const clean_img = () => {
            return del(`${config.dist}/img`)
        };
        
      • import {src, dest} from 'gulp';
        import concat from 'gulp-concat';
        import sassGlob from 'gulp-sass-glob';
        import sassCompile from 'gulp-sass';
        import autoprefixer from 'gulp-autoprefixer';
        import rename from 'gulp-rename';
        import config from '../../config.json';
              
        export const css_libraries = () => {
            return src([
                './node_modules/normalize.css/normalize.css',
            ])
                .pipe(concat(config.libs.scss))
                .pipe(dest(`${config.src}/scss/libs`))
        }
              
        export const sass = () => {
            return src(`${config.src}/scss/**/*.{scss, sass}`, {sourcemaps: true})
                .pipe(sassGlob())
                .pipe(sassCompile({
                    errLogToConsole: true,
                    outputStyle: 'compressed'
                }).on('error', sassCompile.logError))
                .pipe(autoprefixer({
                    overrideBrowserslist: config.autoprefixer,
                    remove: false,
                    cascade: false
                }))
                .pipe(rename({
                    extname: '.min.css'
                }))
                .pipe(dest(`${config.dist}/css`, {sourcemaps: '.'}))
        }
        
      • import {src} from 'gulp';
        import ghPages from 'gulp-gh-pages';
        import config from '../../config.json';
              
        export const source_deploy = () => {
            return src([
                `${config.dist}/**/*`,
                `!${config.dist}/**/*.map`
            ])
                .pipe(ghPages({
                    message: config.deployMessage
                }))
        }
        
      • import fs from "fs";
        import {src, dest} from 'gulp';
        import ttf2woff from 'gulp-ttf2woff';
        import ttf2woff2 from 'gulp-ttf2woff2';
        import fonter from 'gulp-fonter';
        import config from '../../config.json';
              
        export const ttf2ttf = () => {
            return src(`${config.src}/fonts/*.ttf`)
                .pipe(dest(`${config.dist}/fonts/`))
        }
              
        export const ttfToWoff = () => {
            src(`${config.src}/fonts/*.ttf`)
                .pipe(ttf2woff())
                .pipe(dest(`${config.dist}/fonts/`))
            return src(`${config.src}/fonts/*.ttf`)
                .pipe(ttf2woff2())
                .pipe(dest(`${config.dist}/fonts/`))
        }
              
        export const otf2ttf = () => {
            return src(`${config.src}/fonts/*.otf`)
                .pipe(fonter({
                    formats: ['ttf']
                }))
                .pipe(dest(`${config.dist}/fonts/`))
        }
              
        export const fontStyle = (done) => {
            fs.writeFileSync(`${config.src}/scss/common/_fonts.scss`, '');
            fs.readdir(`${config.dist}/fonts/`, function (err, items) {
                if (items) {
                    let c_fontname;
                    for (var i = 0; i < items.length; i++) {
                        let fontname = items[i].split('.');
                        fontname = fontname[0];
                        if (c_fontname != fontname) {
                            fs.appendFileSync(`${config.src}/scss/common/_fonts.scss`, '@include font("' + fontname + '", "' + fontname + '", "400", "normal");\r\n');
                        }
                        c_fontname = fontname;
                    }
                }
            })
            done();
        }
        
      • import fs from "fs";
        import path from "path";
        import {src, dest} from 'gulp';
        import ejs from 'gulp-ejs';
        import gitRepoInfo from 'git-repo-info';
        import gitLog from 'gitlog';
        import cheerio from 'cheerio';
        import config from '../../config.json';
              
        export const process_html = () => {
            return src(`${config.src}/html/**/*.html`)
               .pipe(ejs(config.ejsVars))
               .pipe(dest(`${config.dist}/html`))
               .pipe(browserSync.stream())
        }
              
        export const make_indexfile = () => {
            const dPath = `${config.src}/html/`, // index를 생성할 파일들이 있는 저장소
                info = gitRepoInfo(), // git 정보 생성
                fileInfo = fs.readdirSync(dPath); // 파일 목록 불러오는 함수를 동기적으로 수정
            let normalFiles = []; // 파일 정보를 저장할 배열 생성
              
            fileInfo.map(function (file) {
                return path.join(dPath, file);
            }).filter(function (file) {
                return fs.statSync(file).isFile();
            }).forEach(function (file) {
                let stats = fs.statSync(file);
                //HTML 파일만 거르기
                let extname = path.extname(file),
                    basename = path.basename(file);
                if (extname == '.html') {
                    // 일반 file info를 저장할 객체 생성
                    let nfileData = {};
                    // title 텍스트 값 추출
                    let fileInnerText = fs.readFileSync(file, 'utf8');
                    let $ = cheerio.load(fileInnerText);
                    let wholeTitle = $('title').text(),
                        splitTitle = wholeTitle.split(' : ');
                    // 객체에 데이터 집어넣기
                    nfileData.title = splitTitle[0];
                    nfileData.name = basename;
                    nfileData.category = String(nfileData.name).substring(0, 2);
                    nfileData.categoryText = splitTitle[1];
                    nfileData.mdate = new Date(stats.mtime);
                    // 파일수정시점 - 대한민국 표준시 기준
                    nfileData.ndate = nfileData.mdate.toLocaleString('ko-KR', {timeZone: 'Asia/Seoul'}) + ' (GMT+9)';
                    // title 마지막 조각 , 인덱스에 붙은 라벨 식별 및 yet 인 경우 수정날짜정보 제거
                    nfileData.status = splitTitle[2];
                    if (typeof splitTitle[2] == 'undefined' || splitTitle[2] == null || splitTitle[2] == '') {
                        nfileData.status = '';
                    } else if (splitTitle[2] == 'yet') {
                        nfileData.mdate = '';
                        nfileData.ndate = '';
                    }
                    normalFiles.push(nfileData);
                }
            });
              
            const gitOptions = {
                repo: __dirname,
                number: 20,
                fields: ["hash", "abbrevHash", "subject", "body", "authorName", "authorDateRel", "committerDate", "committerDateRel"],
                execOptions: {maxBuffer: 1000 * 1024},
            };
              
            let commits;
              
            try {
                commits = gitLog(gitOptions).reverse();
            } catch (err) {
                console.log(err);
            }
              
            let log;
              
            if (commits) {
                log = true;
                for (let i = 0; i < normalFiles.length; i++) {
                    for (let j = 0; j < commits.length; j++) {
                        let boolean = commits[j].files.filter((x) => {
                            if (path.extname(x) === '.html') return x
                        }).map((x) => path.basename(x)).some(x => x === normalFiles[i].name);
                        if (boolean) {
                            normalFiles[i].committerDate = new Date(commits[j].committerDate).toLocaleDateString();
                            normalFiles[i].abbrevHash = commits[j].abbrevHash;
                        }
                    }
                }
            } else {
                log = false;
            }
              
            let projectObj = {
                nfiles: normalFiles,
                branch: info.branch,
                commits: log,
            }
            let projectObjStr = JSON.stringify(projectObj);
            let projectObjJson = JSON.parse(projectObjStr);
              
            //index 파일 쓰기
            return src('index.html')
                .pipe(ejs(projectObjJson))
                .pipe(dest(config.dist))
        }
        
      • import fs from "fs";
        import path from "path";
        import {src, dest, lastRun} from 'gulp';
        import imagemin from 'gulp-imagemin';
        import spritesmith from 'gulp.spritesmith-multi';
        import svgSprite from 'gulp-svg-sprite';
        import sort from 'gulp-sort';
        import pngquant from 'gulp-pngquant';
        import vinylBuffer from 'vinyl-buffer';
        import merge from 'merge-stream';
        import config from '../../config.json';
              
        export const optimize_imgs = () => {
            return src([
                `${config.src}/img/**/*.{png, gif, jpg, svg}`,
                `!${config.src}/img/sprites/**/*`,
                `!${config.src}/img/sprites-svg/**/*`
            ], { since: lastRun(optimize_imgs) })
                .pipe(imagemin([
                    imagemin.gifsicle({interlaced: true}),
                    imagemin.mozjpeg({quality: 75, progressive: true}),
                    imagemin.optipng({optimizationLevel: 5}),
                    imagemin.svgo({
                        plugins: [
                            {removeViewBox: true},
                            {cleanupIDs: false}
                        ]
                    })
                ], {
                    verbose: true
                }))
                .pipe(dest(`${config.dist}/img/`))
        }
              
        export const sprites = () => {
            const opts = {
                spritesmith: function (options, sprite, icons) {
                    options.imgPath = `../img/${options.imgName}`;
                    options.cssName = `_${sprite}-mixins.scss`;
                    options.cssTemplate = `${config.src}/scss/vendor/spritesmith-mixins.handlebars`;
                    options.cssSpritesheetName = sprite;
                    options.padding = 4;
                    options.algorithm = 'binary-tree';
                    return options
                }
            };
              
            const spriteData = src(`${config.src}/img/sprites/**/*.png`)
                .pipe(spritesmith(opts)).on('error', function (err) {
                    console.log(err)
                });
              
            const imgStream = spriteData.img
                .pipe(vinylBuffer())
                .pipe(pngquant({
                    quality: '90'
                }))
                .pipe(dest(`${config.dist}/img`));
              
            const cssStream = spriteData.css
                .pipe(dest(`${config.src}/scss/vendor`));
              
            return merge(imgStream, cssStream)
        }
              
        export const spriteSvg = (done) => {
            const svgPath = `${config.src}/img/sprites-svg`,
                folders = fs.readdirSync(svgPath).filter((file) => fs.statSync(path.join(svgPath, file)).isDirectory()),
                options = {
                    spritesmith: (options) => {
                        const {folder, config} = options;
                        return {
                            shape: {
                                spacing: {
                                    padding: 4
                                },
                                id: {
                                    generator: function (name) {
                                        return path.basename(name.split(`${config.src}/scss/vendor`).join(this.separator), '.svg');
                                    }
                                }
                            },
                            mode: {
                                css: {
                                    dest: './',
                                    bust: false,
                                    sprite: folder + '-svg.svg',
                                    render: {
                                        scss: {
                                            template: path.join(`${config.src}/scss/vendor`, 'sprite-svg-mixins.handlebars'),
                                            dest: path.posix.relative(`${config.src}/img`, path.posix.join(`${config.src}/scss`, 'vendor', '_' + folder + '-svg-mixins.scss'))
                                        }
                                    }
                                }
                            },
                            variables: {
                                spriteName: folder,
                                baseName: path.posix.relative(`${config.src}/css`, path.posix.join(`${config.src}/img`, folder + '-svg')),
                                svgToPng: ''
                            }
                        }
                    }
                }
              
            folders.map((folder) => {
                return new Promise((resolve) => {
                    src(path.join(`${config.src}/img/sprites-svg`, folder, '*.svg'))
                        .pipe(sort())
                        .pipe(svgSprite(options.spritesmith({folder, config})))
                        .pipe(dest(`${config.src}/img`))
                        .on('end', resolve);
                });
            });
            done();
        }
        
      • import {src, dest} from 'gulp';
        import concat from 'gulp-concat';
        import babel from 'gulp-babel';
        import uglify from 'gulp-uglify';
        import rename from 'gulp-rename';
        import esLint from 'gulp-eslint';
        import config from '../../config.json';
              
        export const eslint = () => {
            return src(`${config.src}/js/*.js`)
                .pipe(esLint())
                .pipe(esLint.format())
                .pipe(esLint.failAfterError());
        }
              
        export const script = () => {
            return src(`${config.src}/js/*.js`, {sourcemaps: true})
                .pipe(concat('script.js'))
                .pipe(babel())
                .pipe(uglify())
                .pipe(rename({suffix: '.min'}))
                .pipe(dest(`${config.dist}/js`, {sourcemaps: '.'}))
        }
              
        export const libs = () => {
            return src([
                './node_modules/jquery/dist/jquery.min.js',
            ])
                .pipe(concat(config.libs.js))
                .pipe(dest(`${config.src}/js/libs`))
                .pipe(dest(`${config.dist}/js/libs`))
        };
        
      • import browser from 'browser-sync';
        import config from '../../config.json';
              
        const browserSync = browser.create();
              
        export const browserSyncReload = (done) => {
            browserSync.reload();
            done();
        }
              
        export const server = () => {
            // serve files from the build folder
            browserSync.init({
                port: 8030,
                ui: {
                    port: 8033,
                    weinre: {
                        port: 8133
                    }
                },
                cors: false, // if you need CORS, set true
                server: {
                    baseDir: `${config.dist}/`
                }
            });
              
            console.log('\x1b[32m%s\x1b[0m', '[--:--:--] HTML/SCSS watch complete...');
        };
        
      • import {parallel, series, watch} from "gulp";
        import config from "../../config.json";
        import {clean_css, clean_fonts, clean_html, clean_img, clean_js} from "../clean";
        import {sprites, spriteSvg} from "../images";
        import {sass} from "../css";
        import {fontStyle, otf2ttf, ttf2ttf, ttfToWoff} from "../fonts";
        import {eslint, libs, script} from "../js";
        import {make_indexfile, process_html} from "../html";
        import {browserSyncReload} from "../server";
              
        export const gulpWatch = () => {
            watch(`${config.src}/img/**/*`, series(clean_img, parallel(spriteSvg, sprites), sass, browserSyncReload));
            watch(`${config.src}/fonts/`, series(clean_fonts, parallel(ttf2ttf, ttfToWoff, otf2ttf), fontStyle, sass, browserSyncReload))
            watch([
                `${config.src}/scss/**/*`,
                `!${config.src}/scss/libs/${config.libs.scss}`
            ], series(clean_css, sass, browserSyncReload));
            watch([
                `${config.src}/js/**/*`,
                `!${config.src}/js/libs/${config.libs.js}`
            ], series(clean_js, eslint, parallel(script, libs), browserSyncReload));
            watch(`${config.src}/html/**/*`, series(clean_html, parallel(make_indexfile, process_html), browserSyncReload));
            watch('index.html', series(make_indexfile, browserSyncReload));
        }
        
      • import {src, dest} from 'gulp';
        import zip from 'gulp-zip';
        import packageJson from '../../package.json'
        import config from '../../config.json';
              
        export const zipFile = () => {
            const date = new Date(),
                dateFormatted = `${date.getFullYear()}${('0' + (date.getMonth() + 1)).slice(-2)}${('0' + date.getDate()).slice(-2)}T${('0' + date.getHours()).slice(-2)}${('0' + date.getMinutes()).slice(-2)}`;
            return src([
                `${config.dist}/**/*`,
                `!${config.dist}/**/*.zip`
            ])
                .pipe(zip(`${packageJson.name}_${packageJson.version}_${dateFormatted}.zip`))
                .pipe(dest(config.dist))
        }
        
    • import {series, parallel} from 'gulp';
      import {clean_dist} from './clean';
      import {script, libs, eslint} from './js';
      import {sass, css_libraries} from './css';
      import {source_deploy} from './deploy';
      import {ttfToWoff, ttf2ttf, otf2ttf, fontStyle} from './fonts';
      import {process_html, make_indexfile} from './html';
      import {optimize_imgs, spriteSvg, sprites} from './images';
      import {server} from './server';
      import {gulpWatch} from './watch';
      import {zipFile} from './zip';
          
      exports.default = series(clean_dist, parallel(css_libraries, optimize_imgs, spriteSvg, sprites, ttfToWoff, otf2ttf, ttf2ttf), fontStyle,
          sass, eslint, parallel(script, libs, make_indexfile, process_html), parallel(server, gulpWatch));
          
      exports.build = series(clean_dist, parallel(css_libraries, optimize_imgs, spriteSvg, sprites, ttfToWoff, otf2ttf, ttf2ttf), fontStyle,
          sass, eslint, parallel(script, libs, make_indexfile, process_html), zipFile);
          
      exports.deploy = series(clean_dist, parallel(css_libraries, optimize_imgs, spriteSvg, sprites, ttfToWoff, otf2ttf, ttf2ttf), fontStyle,
          sass, eslint, parallel(script, libs, make_indexfile, process_html), zipFile, source_deploy);
      
      • *.ttf
      • *.otf
      •   <!DOCTYPE html>
          <html lang="ko">
          <%- include('includes/head.ejs', {
              title: "<title>Test : Title1</title>",
          })%>
          <body>
          안녕, 수정, 실시간반영
          <textarea name="" id="" cols="30" rows="10"></textarea>
          <textarea name="" id="" cols="30" rows="10"></textarea>
          </body>
          </html>
        
      • /common/
      • /sprites/
        • /sprite/
      • /sprites-svg/
        • /sprite/
      • /libs/
        • @charset "utf-8";
                  
          $root-em: 10;
          // enable-IE8 속성은 IE8에서 rem을 지원하지 않기 때문에 일반 px unit로 폴백을 하기 위해 존재합니다.
          $enable-IE8: false !default;
          $initial-font-size: 15;
          $initial-line-height: 19;
                  
          $font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !default;
          $background-body-color: #fff;
          $line_green: #00c721;
                  
          * {
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
            -webkit-focus-ring-color: rgba(0, 0, 0, 0);
          }
                  
          *:not(input):not(textarea) {
            -webkit-touch-callout: none; // disable the IOS popup when long-press on a link
            user-select: none;
          }
                  
          body, p, h1, h2, h3, h4, h5, h6, ul, ol, li, dl, dt, dd, table, th, td, form, fieldset, legend, input, textarea, button, select {
            margin: 0;
            padding: 0;
          }
                  
          body, input, textarea, select, button, table {
            outline: none !important;
          }
                  
          html, body {
            height: 100%;
            height: 100vh;
          }
                  
          html {
            font-size: $root-em * 1px;
            line-height: $root-em * 1px;
          }
                  
          body {
            -webkit-overflow-scrolling: touch;
            user-select: none;
            background-color: $background-body-color;
            @include rfonts($initial-font-size, $initial-line-height, 400);
          }
                  
          html, body, code, kbd, pre, samp {
            font-family: $font-family;
          }
                  
          article, aside, dialog, footer, header, section, nav, figure, main {
            display: block
          }
                  
          pre {
            white-space: pre-wrap;
            word-wrap: break-word;
          }
                  
          em, address {
            font-style: normal;
          }
                  
          img, fieldset {
            border: 0;
          }
                  
          ul, ol, dl {
            list-style: none;
          }
                  
          a {
            color: inherit;
            text-decoration: none;
          }
                  
          .blind {
            @extend %visuallyhidden;
          }
          
        • _fonts.scss
        • @charset "utf-8";
                  
          // hidden text > @extend %visuallyhidden;
          %visuallyhidden {
            margin: -1px !important;
            padding: 0 !important;
            width: 1px;
            height: 1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            position: absolute;
          }
                  
          // clear:both 대체 >> @extend %clearfix;
          %clearfix {
            *zoom: 1;
            &:before, &:after {
              content: " ";
              display: table;
            }
            &:after {
              clear: both;
            }
          }
                  
          %clearfix-under {
            *zoom: 1;
            &:after {
              content: " ";
              display: table;
              clear: both;
            }
          }
                  
          // mixin:opacity > @include opacity(0.8);
          @mixin opacity($opacity) {
            opacity: $opacity;
            $opacity-ie: $opacity * 100;
            filter: alpha(opacity = $opacity-ie);
          }
                  
          // mixin:fonts > @include fonts(14,18,300);
          @mixin fonts($size: 14px, $lineHeight: false, $bold: false) {
            @if unitless($size) {
              // @warn "Assuming #{$size} to be in pixels";
              $size: 1px * $size;
            }
            font-size: $size;
            // if unit(px) in lineheight, no calc and use raw lineheight
            // if none : no write
            $lhr: 0;
            @if $lineHeight {
              @if unitless($lineHeight) {
                // @warn "Assuming #{$size} to be in pixels";
                $lineHeight: 1px * $lineHeight;
                // $pow: pow(10, 2);
                $lh: round($lineHeight / $size * 100) / 100;
                $lhr: $lh * 1em;
              }
              @else {
                $lhr: $lineHeight;
              }
              line-height: $lhr;
            }
            // bold : if none - no write
            @if $bold {
              @if $bold == "thin" {
                font-weight: 100;
              }
              @else if $bold == "light" {
                font-weight: 300;
              }
              @else if $bold == "normal" {
                font-weight: 400;
              }
              @else if $bold == "bold" {
                font-weight: 700;
              }
              @else {
                font-weight: $bold;
              }
            }
          }
                  
          // rem unit fonts mixin >> @include rfonts(14,18,300); or @include rfonts(14,18px,"normal");
          @mixin rfonts($size: $initial-font-size, $lineHeight: false, $bold: false) {
            $lhr: 0;
            @if $enable-IE8 {
              font-size: $size * 1px;
            }
            font-size: setRem($size);
            @if $lineHeight {
              @if unitless($lineHeight) {
                $lhr: setRem($lineHeight);
                @if $enable-IE8 {
                  line-height: $lineHeight * 1px;
                }
                line-height: $lhr;
              }
              @else {
                $lhr: $lineHeight;
                line-height: $lhr;
              }
            }
            @if $bold {
              font-weight: setFontWeight($bold);
            }
          }
                  
          @mixin font($font_name, $file_name, $weight, $style) {
            @font-face {
              font-family: $font_name;
              font-display: swap;
              src: url("../fonts/#{$file_name}.woff2") format("woff2"), url("../fonts/#{$file_name}.woff") format("woff"), url("../fonts/#{#file_name}.ttf") format("truetype");
              font-weight: #{$weight};
              font-style: #{$style};
            }
          }
                  
          @function setRem($size) {
            // $pow: pow(10, 2);
            $remSize: round($size / $root-em * 100) / 100;
            @return $remSize * 1rem;
          }
                  
          @function setFontWeight($bold) {
            @if $bold=="thin" {
              @return 100;
            }
            @else if $bold=="light" {
              @return 300;
            }
            @else if $bold=="normal" {
              @return 400;
            }
            @else if $bold=="bold" {
              @return 700;
            }
            @else {
              @return $bold;
            }
          }
                  
          // mixin:allauto
          @mixin all-auto {
            position: static;
            width: auto;
            height: auto;
            height: auto;
            width: auto;
          }
                  
          @mixin ellipsis {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
                  
          // 여러줄 말줄임 (only. webkit)
          @mixin ellipsis-multiline($line) {
            overflow: hidden;
            display: -webkit-box;
            -webkit-line-clamp: $line;
            -webkit-box-orient: vertical;
            word-break: break-all;
          }
                  
          @mixin vertical-middle {
            top: 50%;
            transform: translateY(-50%);
          }
                  
          %imageButton {
            display: inline-block;
            box-sizing: border-box;
            border: 0;
            background-color: transparent;
            &>span {
              @extend %visuallyhidden;
            }
          }
                  
          %textButton {
            display: inline-block;
            box-sizing: border-box;
            border: 0;
            background-color: transparent;
          }
                  
          @mixin border-radius($border) {
            border-top-left-radius: $border;
            border-top-right-radius: $border;
            border-bottom-left-radius: $border;
            border-bottom-right-radius: $border;
          }
                  
          @mixin border-top-radius($radius) {
            -webkit-border-top-left-radius: $radius;
            -webkit-border-top-right-radius: $radius;
            -moz-border-radius-topleft: $radius;
            -moz-border-radius-topright: $radius;
            border-top-left-radius: $radius;
            border-top-right-radius: $radius;
          }
                  
          @mixin border-bottom-radius($radius) {
            -webkit-border-bottom-left-radius: $radius;
            -webkit-border-bottom-right-radius: $radius;
            -moz-border-radius-bottomleft: $radius;
            -moz-border-radius-bottomright: $radius;
            border-bottom-left-radius: $radius;
            border-bottom-right-radius: $radius;
          }
                  
          @mixin positionFixed {
            -webkit-backface-visibility: hidden;
            -webkit-transform: translate3d(0, 0, 0);
            -webkit-user-select: none;
            -webkit-touch-callout: none;
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
            overflow-y: visible !important;
          }
                  
          @mixin border-box {
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
          }
                  
          @mixin wordBreak {
              word-break: break-all;
              word-break: break-word;
          }
                  
          @mixin hidpi($ratio: 1.3) {
            @media only screen and (min-resolution: round($ratio * 96dpi)),
            only screen and (min-resolution: $ratio * 1dppx) {
              @content;
            }
          }
                  
          // z-index 관리
          @function z($name) {
            @if index($z-indexes, $name) {
              @return (length($z-indexes) - index($z-indexes, $name)) + 1;
            } @else {
              @warn 'There is no item "#{$name}" in this list; choose one of: #{$z-indexes}';
              @return null;
            }
          }
                  
          // 0.5 라인 > @extend %bg_t;
          //%bg_t {
          //  content: '';
          //  position: absolute;
          //  top: -1px;
          //  left: 0;
          //  width: 100%;
          //  height: 1px;
          //  border-width: 0 0 1px 0;
          //  border-style: solid;
          //  transform: scaleY(0.5);
          //}
          //
          //%bg_u {
          //  content: '';
          //  position: absolute;
          //  left: 0;
          //  bottom: -1px;
          //  width: 100%;
          //  height: 1px;
          //  border-width: 0 0 1px 0;
          //  border-style: solid;
          //  transform: scaleY(0.5);
          //}
                  
          //
          // safe area
          // - @include safeArea(true, padding, 10px 5px right left);
          // - @include safeArea(false, padding, 5px bottom);
          // - $all: true / false(multi / single)
          // - $attr: selector
          // - $val...: value direction
          //
          @mixin safeArea($all: false, $attr: padding, $val...) {
            @if(($attr == padding or $attr == margin) and $all == false) {
              #{$attr}-#{separateVal($val, false)}: safeAresLoop($all, 'env', $attr, $val);
              #{$attr}-#{separateVal($val, false)}: safeAresLoop($all, 'constant', $attr, $val);
            } @else {
              #{$attr}: safeAresLoop($all, 'env', $attr, $val);
              #{$attr}: safeAresLoop($all, 'constant', $attr, $val);
            }
          }
                  
          //
          // slice dircetion value
          // - $boolean: true/false (value / direction)
          //
          @function separateVal($val, $boolean) {
            $val: nth($val, 1);
            $returnVal: 0;
            $returnStringVal: 0;
            $stringIdx: 0;
            @for $i from 1 through length($val) {
              @if(type-of(nth($val, $i)) != string and $i == 1) {
                $returnVal: nth($val, $i);
              } @else if(type-of(nth($val, $i))!=string and $i != 1) {
                $returnVal: join($returnVal, nth($val, $i));
              }  @else if(type-of(nth($val, $i)) == string and $stringIdx == 0) {
                $stringIdx: $i;
                $returnStringVal: nth($val, $i);
              } @else {
                $returnStringVal: join($returnStringVal, nth($val, $i));
              }
            }
                  
            @if ($boolean == true) {
              @return $returnVal;
            } @else {
              @return $returnStringVal;
            }
          }
                  
          @function dirFlag($receiveDirFlag, $receiveDir) {
            @if($receiveDirFlag != length($receiveDir)) {
              @return $receiveDirFlag+1;
            }
            @return $receiveDirFlag;
          }
                  
          @function safeAresLoop($all, $set, $attr, $val) {
            $dir: top, right, bottom, left;
            $receiveDir: separateVal($val, false);
            $receiveDirFlag: 1;
            $realVal: separateVal($val, true);
            $returnVal: '';
            $safeArea: '';
                  
            // value 축약형 변환
            @if(length($realVal)==1) {
              $realVal: $realVal $realVal $realVal $realVal;
            } @else if(length($realVal)==2) {
              $realVal: nth($realVal, 1) nth($realVal, 2) nth($realVal, 1) nth($realVal, 2);
            } @else if(length($realVal)==3) {
              $realVal: nth($realVal, 1) nth($realVal, 2) nth($realVal, 3) nth($realVal, 2);
            } @else if(length($realVal)==4) {
              $realVal: $realVal;
            } @else {
              @error 'safeAresLoop length of $val error';
            }
                  
            @for $i from 1 through 4 {
              // check $set (env or constant)
              @if($set==env) {
                $safeArea: env(safe-area-inset-#{nth($dir, $i)});
              } @else if($set==constant) {
                $safeArea: constant(safe-area-inset-#{nth($dir, $i)});
              } @else {
                @error 'safeAresLoop $set error';
              }
                  
              // returnVal 초기 값
              @if($i == 1) {
                @if((nth($dir, $i) == nth($receiveDir, $receiveDirFlag)) == true and nth($realVal, $i) == 0) {
                  $returnVal: $safeArea;
                  $receiveDirFlag: dirFlag($receiveDirFlag, $receiveDir);
                } @else if((nth($dir, $i) == nth($receiveDir, $receiveDirFlag)) == true and nth($realVal, $i) != 0) {
                  $returnVal: calc(#{nth($realVal, $i)} + #{$safeArea});
                  $receiveDirFlag: dirFlag($receiveDirFlag, $receiveDir);
                } @else {
                  $returnVal: nth($realVal, $i);
                }
              } @else {
                // sare-area multi
                @if($receiveDir != 0 and $all == true) {
                  @if((nth($dir, $i) == nth($receiveDir, $receiveDirFlag)) == true and nth($realVal, $i) == 0) {
                    $returnVal: join($returnVal, $safeArea);
                    $receiveDirFlag: dirFlag($receiveDirFlag, $receiveDir);
                  } @else if((nth($dir, $i) == nth($receiveDir, $receiveDirFlag)) == true and nth($realVal, $i) != 0) {
                    $returnVal: join($returnVal, calc(#{nth($realVal, $i)} + #{$safeArea}));
                    $receiveDirFlag: dirFlag($receiveDirFlag, $receiveDir);
                  } @else {
                    $returnVal: join($returnVal, nth($realVal, $i));
                  }
                } @else if($receiveDir != 0 and $all == false) { // sare-area single
                  @if((nth($dir, $i) == nth($receiveDir, $receiveDirFlag)) == true) {
                    @if(nth($realVal, $i) == 0) {
                      $returnVal: $safeArea;
                    } @else {
                      $returnVal: calc(#{nth($realVal, $i)} + #{$safeArea});
                    }
                  }
                } @else {   // safe-area 방향 없을 때
                  $returnVal: nth($realVal, $i);
                  @warn 'please set drection of safe-area';
                  @return $returnVal;
                }
              }
            }
            @return $returnVal;
          }
          
      • /libs/
      • /service/
        •         
          ${{spriteName}}-svg-origin: (
          total-width: {{spriteWidth}}px,
          total-height: {{spriteHeight}}px,
          padding: {{padding.top}}px,
          imageSrc: '{{{baseName}}}.svg'
          );
                  
          ${{spriteName}}-svg-vars: (
          {{#shapes}}
            '{{name}}': (
            offset-x: {{position.absolute.x}}px,
            offset-y: {{position.absolute.y}}px,
            width: {{width.inner}}px,
            height: {{height.inner}}px
            ),
          {{/shapes}}
          );
                  
          @mixin useSvg-{{spriteName}} ($image, $size: false) {
          {{!-- 변수 --}}
          $image-origin: ${{spriteName}}-svg-origin;
          $image-var: map-get(${{spriteName}}-svg-vars, $image);
          $image-path: map-get($image-origin, 'imageSrc');
          $padding: map-get(${{spriteName}}-svg-origin, 'padding');
          $aspectRatio: map-get($image-var, 'height') / map-get($image-var, 'width');
          $sizeRatio: $size / map-get($image-var, 'width');
                  
          {{!-- 적용 --}}
          @if($size){
          @if(unitless($size)){
          @warn '"#{$size}" of "#{$image}" is unitless value. Please insert value with unit(px)';
          }
          @else {
          width: $size;
          height: round($size * $aspectRatio * 100)/100;
          background-image: url($image-path);
          background-size: round(map-get($image-origin, 'total-width') * $sizeRatio * 100)/100 round(map-get($image-origin, 'total-height') * $sizeRatio * 100)/100;
          background-position: round((map-get($image-var, 'offset-x') - $padding) * $sizeRatio * 100)/100 round((map-get($image-var, 'offset-y') - $padding) * $sizeRatio * 100)/100;
          }
          }
          @else {
          width: map-get($image-var, 'width');
          height: map-get($image-var, 'height');
          background-image: url($image-path);
          background-size: map-get($image-origin, 'total-width') map-get($image-origin, 'total-height');
          background-position: (map-get($image-var, 'offset-x') - $padding) (map-get($image-var, 'offset-y') - $padding);
          }
          background-repeat: no-repeat;
          }
                  
          
        •         
          {
          	'functions': true
          }
                  
          ${{spritesheet_info.name}}-vars: (
          	{{#items}}
          	'{{name}}': (
          		offset-x: {{px.offset_x}} / 2,
          		offset-y: {{px.offset_y}} / 2,
          		width: {{px.width}} / 2,
          		height: {{px.height}} / 2,
          		total-width: {{px.total_width}} / 2,
          		total-height: {{px.total_height}} / 2,
          		imageSrc: '{{{escaped_image}}}'
          	),
          	{{/items}}
          );
          ${{spritesheet_info.name}}-origin: (
            total-width: {{spritesheet.px.width}} / 2,
            total-height: {{spritesheet.px.height}} / 2,
            imageSrc: '{{spritesheet.escaped_image}}'
          );
                  
          {{#options.functions}}
          @mixin sprite-size($image) {
          	background-size: map-get($image, 'total-width') map-get($image, 'total-height');
          }
                  
          @mixin sprite-image($image) {
          	$image-path: map-get($image, 'imageSrc');
          	background-image: url($image-path);
          }
                  
          @mixin set-{{spritesheet_info.name}} {
            @include sprite-size(${{spritesheet_info.name}}-origin);
            @include sprite-image(${{spritesheet_info.name}}-origin);
          }
                  
          @mixin use-{{spritesheet_info.name}}($image, $size: true) {
            @include set-{{spritesheet_info.name}};
          	background-position: map-get(map-get(${{spritesheet_info.name}}-vars, $image), 'offset-x') map-get(map-get(${{spritesheet_info.name}}-vars, $image), 'offset-y');
          	@if $size {
          		width: map-get(map-get(${{spritesheet_info.name}}-vars, $image), 'width');
          		height: map-get(map-get(${{spritesheet_info.name}}-vars, $image), 'height');
          	}
          }
          {{/options.functions}}
                  
          
      • @charset "utf-8";
              
        @import "vendor/*-mixins.scss";
        @import "common/mixins";
        @import "common/fonts";
        @import "common/common";
        @import "libs/libs";
              
        @import "service/style";
        
  • {  
    "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": {
      }
    }
    
  • node_modules
      
    package-lock.json
      
    dist
    
  •   {
        "autoprefixer": ["> 1%", "last 2 versions", "iOS 10", "Android 2.3", "Firefox ESR", "IE 11"],
        "deployMessage": "[UPDATE] deploy to gh-pages",
        "spriteHash": true,
        "ejsVars": {},
        "src": "./src",
        "dist": "./dist",
        "libs": {
          "scss": "_libs.scss",
          "js": "libs.min.js"
        }
      }
    
  • <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no,viewport-fit=cover">
        <title>Test</title>
        <style>a,body{color:#000}body,li{position:relative}body,button,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,select,table,td,textarea,th,ul{margin:0;padding:0;-webkit-text-size-adjust:none}body,button,input,select,table,textarea{font-family:-apple-system,system-ui,BlinkMacSystemFont,Roboto,sans-serif;font-size:14px;line-height:1.25em}body{padding:0 1em;background-color:#fff;word-wrap:break-word;word-break:break-all}fieldset,img{border:0}ol,ul{list-style:none}a{text-decoration:none;cursor:pointer}h1{padding:.8em 4em .6em .1em;border-bottom:3px solid #222;background:#fff;font-size:1.2em}h2,h3{clear:both;font-size:1em}h2{padding:2em .1em .4em;border-bottom:1px solid #000}h3{padding:1em .1em .4em;border-bottom:1px dotted #888}.inf{padding:10px 2px 0;line-height:1.4em;color:#666}ul{margin-bottom:10px;font-size:.9em}li{border-bottom:1px solid #dfdfdf}li .na{display:block;padding:.7em 31px .6em .1em;color:#999}li .bx{display:inline-block;border:1px solid #999;background:#f2f2f2;font-size:.8em;padding:1px 3px;color:#999;border-radius:3px}li a{display:block;padding:.7em 31px .6em .1em;background:url(http://static.naver.com/www/m/cm/im/bu_lk.gif) 100% 50% no-repeat}li a span{margin-right:5px;font-weight:700;font-size:.85em}li a .sup{color:#a30}li .inner_btn{position:absolute;top:4px;right:35px;z-index:99;padding:0 5px;border:1px solid #da7c0c;background:#f78d1d;background:-webkit-gradient(linear,0 0,0 100%,from(#faa51a),to(#f47a20));font-size:11px;line-height:23px;color:#fef4e9;border-radius:3px;box-shadow:1px 1px 0 rgba(0,0,0,.3);-webkit-box-shadow:1px 1px 0 rgba(0,0,0,.3)}li .inner_btn:hover{background:#f47c20;background:-webkit-gradient(linear,0 0,0 100%,from(#f88e11),to(#f06015))}.message{display:block;margin:5px 0 0;font-size:14px;color:red;}.toc{padding:0 20px 20px;background:#efefef}.toc li a{text-transform:capitalize}.sec_h{text-transform:capitalize}.date{display:inline-block;padding:2px 4px;color:#a30;}.yet{color:#cfcdcd}.yet .date{background-color:#ccc;color:#fff}.yet .date:before{content:'YET'}.new .date{background-color:#f50a20;color:#fff}.new .date:before{content:'NEW|'}.update .date{background-color:#3c94e5;color:#fff}.update .date:before{content:'UPDATE|'}.sec_h:target,.sec_h:target+.page-lst{animation-duration:2s;animation-name:highlight;animation-iteration-count:1;}@keyframes highlight{from{background-color:rgba(255,120,0,.2);}to{background-color:rgba(255,120,0,0)}}
        </style>
    </head>
    <body>
      
    <h1>Test <span class="message"><%= branch %> 브랜치</span></h1>
      
    <p class="inf">Test Index File</p>
      
    <img src="" id="_im1" width="150" height="150" style="width:150px;height:150px">
    <!--<script>document.getElementById("_im1").src = 'http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl='+encodeURIComponent(location.href);</script>-->
      
    <div class="toc">
        <h2>카테고리 바로가기</h2>
        <ul>
            <% for (var i=0; i < 100 ; i++){
            var categoryNum = String("0" + i).slice(-2);
            var colIndex = 0;
            var nfileList = nfiles;
            for(var j=0 ; j < nfileList.length; j++){
            if (categoryNum == nfileList[j].category){
            if (colIndex == 0){ %>
            <li><a href="#tab<%= categoryNum %>"><%= nfileList[j].categoryText %></a></li><%}
            colIndex++;
            }
            }
            }%>
        </ul>
    </div>
      
    <%
    var today = new Date();
    today = Date.parse(today);
    for (var i=0; i < 100 ; i++){
    var categoryNum = String("0" + i).slice(-2);
    var listCount = 0;
    var nfileList = nfiles;
    var lastCategory = nfileList[nfileList.length - 1].category;
    for(var j=0 ; j < nfileList.length; j++){
    if (categoryNum == nfileList[j].category){
    if (listCount == 0){ %>
    <h2 id="tab<%= categoryNum %>" class="sec_h"><%= nfileList[j].categoryText %></h2>
    <ul class="page-lst"><%} %>
        <li>
            <a class="<%= nfileList[j].status %>" href="html/<%= nfileList[j].name %>">
                <%= nfileList[j].title %> / <%= nfileList[j].name %>
                <% if (commits) { %>
                <span class="date" title="<%= nfileList[j].ndate %>">최근 커밋 날짜 <%= nfileList[j].committerDate.substring(2,10).replace(/-/gi,'') %></span>
                <span class="date" title="<%= nfileList[j].ndate %>">최근 커밋 해쉬 <%= nfileList[j].abbrevHash %></span>
                <% } else { %>
                <span class="date" title="<%= nfileList[j].ndate %>">최근 수정 날짜(아직 커밋기록 없음) <%= nfileList[j].mdate.substring(2,10).replace(/-/gi,'') %></span>
                <% } %>
            </a> <% if (nfileList[j].splitStatus) { %>
            <% } %>
        </li> <% listCount++; } }  if (categoryNum <= lastCategory && listCount >= 1) { %>
    </ul> <%  } } %>
      
    <script>
        var unbindEl = document.querySelectorAll('.yet'),
            unbindElLength = unbindEl.length;
        for (i=0;i < unbindElLength; i++) {
            var unbindParent = unbindEl[i].parentNode;
            unbindParent.addEventListener('click', noti);
            function noti(e){
                e.preventDefault();
                alert('작업 진행중인 페이지입니다.');
            }
        }
    </script>
      
    </body>
    </html>
    
  •   {
        "name": "hyungju-lee",
        "version": "1.0.0",
        "description": "LHJ's Gulp File",
        "repository": {
          "type": "git",
          "url": "https://github.com/hyungju-lee/gulp-develop.git"
        },
        "bugs": {
          "url": "https://github.com/hyungju-lee/gulp-develop.git/issue"
        },
        "main": "gulpfile.babel.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": "",
        "license": "ISC",
        "devDependencies": {
          "@babel/core": "^7.9.6",
          "@babel/preset-env": "^7.9.6",
          "@babel/register": "^7.9.0",
          "browser-sync": "^2.26.7",
          "cheerio": "^1.0.0-rc.3",
          "del": "^5.1.0",
          "git-repo-info": "^2.1.1",
          "gitlog": "^4.0.0",
          "gulp": "^4.0.2",
          "gulp-autoprefixer": "^7.0.1",
          "gulp-babel": "^8.0.0",
          "gulp-concat": "^2.6.1",
          "gulp-ejs": "^5.1.0",
          "gulp-eslint": "^6.0.0",
          "gulp-fonter": "^0.3.0",
          "gulp-gh-pages": "^0.5.4",
          "gulp-imagemin": "^7.1.0",
          "gulp-pngquant": "^1.0.13",
          "gulp-rename": "^2.0.0",
          "gulp-sass": "^4.1.0",
          "gulp-sass-glob": "^1.1.0",
          "gulp-sort": "^2.0.0",
          "gulp-svg-sprite": "^1.5.0",
          "gulp-ttf2woff": "^1.1.1",
          "gulp-ttf2woff2": "^3.0.0",
          "gulp-uglify": "^3.0.2",
          "gulp-zip": "^5.0.1",
          "gulp.spritesmith-multi": "^3.1.0",
          "jquery": "^3.5.1",
          "merge-stream": "^2.0.0",
          "normalize.css": "^8.0.1",
          "path": "^0.12.7",
          "vinyl-buffer": "^1.0.1"
        }
      }