- *.ttf
- *.otf
<head> <%- title %> <meta charset="utf-8"> <meta name="format-detection" content="telephone=no"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,minimum-scale=1, user-scalable=no, target-densitydpi=medium-dpi, viewport-fit=cover"> <link rel="stylesheet" href="../css/style.min.css"> <script defer src="../js/libs/libs.min.js"></script> <script defer src="../js/script.min.js"></script> </head>
<!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" } }
'use strict'; import fs from 'fs'; import path from 'path'; import cheerio from 'cheerio'; import {src, dest, series, parallel, watch, lastRun} from 'gulp'; import del from 'del'; import merge from 'merge-stream'; import browser from 'browser-sync'; import vinylBuffer from 'vinyl-buffer'; import spritesmith from 'gulp.spritesmith-multi'; import gitRepoInfo from 'git-repo-info'; import pngquant from 'gulp-pngquant'; import rename from 'gulp-rename'; import sassCompile from 'gulp-sass'; import sassGlob from 'gulp-sass-glob'; import autoprefixer from 'gulp-autoprefixer'; import sort from 'gulp-sort'; import svgSprite from 'gulp-svg-sprite'; import imagemin from 'gulp-imagemin'; import esLint from 'gulp-eslint'; import babel from 'gulp-babel'; import uglify from 'gulp-uglify'; import concat from 'gulp-concat'; import ejs from 'gulp-ejs'; import gitLog from 'gitlog'; import zip from 'gulp-zip'; import ghPages from 'gulp-gh-pages'; import packageJson from './package.json'; import config from './config.json'; import ttf2woff from 'gulp-ttf2woff'; import ttf2woff2 from 'gulp-ttf2woff2'; import fonter from 'gulp-fonter'; const ttf2ttf = () => { return src(`${config.src}/fonts/*.ttf`) .pipe(dest(`${config.dist}/fonts/`)) } 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/`)) } const otf2ttf = () => { return src(`${config.src}/fonts/*.otf`) .pipe(fonter({ formats: ['ttf'] })) .pipe(dest(`${config.dist}/fonts/`)) } 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(); } 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/`)) .pipe(browserSync.stream()) } 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) } 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(); } const css_libraries = () => { return src([ './node_modules/normalize.css/normalize.css', ]) .pipe(concat(config.libs.scss)) .pipe(dest(`${config.src}/scss/libs`)) .pipe(browserSync.stream()) } 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: '.'})) .pipe(browserSync.stream()) } const eslint = () => { return src(`${config.src}/js/*.js`) .pipe(esLint()) .pipe(esLint.format()) .pipe(esLint.failAfterError()); } 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: '.'})) .pipe(browserSync.stream()) } 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`)) .pipe(browserSync.stream()) }; const process_html = () => { return src(`${config.src}/html/**/*.html`) .pipe(ejs(config.ejsVars)) .pipe(dest(`${config.dist}/html`)) .pipe(browserSync.stream()) } 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)) .pipe(browserSync.stream()) } 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}` ]) }; const clean_css = () => { return del([ `${config.dist}/css`, `${config.src}/scss/libs/*`, `!${config.src}/scss/libs/${config.libs.scss}` ]) }; const clean_fonts = () => { return del(`${config.dist}/fonts`) } const clean_js = () => { return del([ `${config.dist}/js`, `${config.src}/js/libs/*`, `!${config.src}/js/libs/${config.libs.js}` ]) }; const clean_html = () => { return del(`${config.dist}/html`) }; const clean_img = () => { return del(`${config.dist}/img`) }; const browserSyncReload = (done) => { browserSync.reload(); done(); } const browserSync = browser.create(), 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...'); }; 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)); } 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)); 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)) } 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); const source_deploy = () => { return src([ `${config.dist}/**/*`, `!${config.dist}/**/*.map` ]) .pipe(ghPages({ message: config.deployMessage })) } 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);
<!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" } }