diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2016-12-29 07:49:51 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2016-12-29 07:49:51 +0900 |
| commit | b3f42e62af698a67c2250533c437569559f1fdf9 (patch) | |
| tree | cdf6937576e99cccf85e6fa3aa8860a1173c7cfb /gulpfile.ts | |
| download | sharkey-b3f42e62af698a67c2250533c437569559f1fdf9.tar.gz sharkey-b3f42e62af698a67c2250533c437569559f1fdf9.tar.bz2 sharkey-b3f42e62af698a67c2250533c437569559f1fdf9.zip | |
Initial commit :four_leaf_clover:
Diffstat (limited to 'gulpfile.ts')
| -rw-r--r-- | gulpfile.ts | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/gulpfile.ts b/gulpfile.ts new file mode 100644 index 0000000000..b01c5f829e --- /dev/null +++ b/gulpfile.ts @@ -0,0 +1,568 @@ +/** + * Gulp tasks + */ + +import * as gulp from 'gulp'; +import * as gutil from 'gulp-util'; +import * as babel from 'gulp-babel'; +import * as ts from 'gulp-typescript'; +import * as tslint from 'gulp-tslint'; +import * as glob from 'glob'; +import * as browserify from 'browserify'; +import * as source from 'vinyl-source-stream'; +import * as buffer from 'vinyl-buffer'; +import * as es from 'event-stream'; +const stylus = require('gulp-stylus'); +const cssnano = require('gulp-cssnano'); +import * as uglify from 'gulp-uglify'; +const ls = require('browserify-livescript'); +const aliasify = require('aliasify'); +const riotify = require('riotify'); +const transformify = require('syuilo-transformify'); +const pug = require('gulp-pug'); +const git = require('git-last-commit'); +import * as rimraf from 'rimraf'; + +const env = process.env.NODE_ENV; +const isProduction = env === 'production'; +const isDebug = !isProduction; + +import { IConfig } from './src/config'; +const config = eval(require('typescript').transpile(require('fs').readFileSync('./src/config.ts').toString())) + ('.config/config.yml') as IConfig; + +const project = ts.createProject('tsconfig.json'); + +gulp.task('build', [ + 'build:js', + 'build:ts', + 'build:copy', + 'build:client' +]); + +gulp.task('rebuild', [ + 'clean', + 'build' +]); + +gulp.task('build:js', () => + gulp.src(['./src/**/*.js', '!./src/web/**/*.js']) + .pipe(babel({ + presets: ['es2015', 'stage-3'] + })) + .pipe(gulp.dest('./built/')) +); + +gulp.task('build:ts', () => + project + .src() + .pipe(project()) + .pipe(babel({ + presets: ['es2015', 'stage-3'] + })) + .pipe(gulp.dest('./built/')) +); + +gulp.task('build:copy', () => { + gulp.src([ + './src/**/resources/**/*', + '!./src/web/app/**/resources/**/*' + ]).pipe(gulp.dest('./built/')); + gulp.src([ + './src/web/about/**/*' + ]).pipe(gulp.dest('./built/web/about/')); +}); + +gulp.task('test', ['lint', 'build']); + +gulp.task('lint', () => + gulp.src('./src/**/*.ts') + .pipe(tslint({ + formatter: 'verbose' + })) + .pipe(tslint.report()) +); + +gulp.task('clean', cb => + rimraf('./built', cb) +); + +gulp.task('cleanall', ['clean'], cb => + rimraf('./node_modules', cb) +); + +gulp.task('default', ['build']); + +const aliasifyConfig = { + aliases: { + 'fetch': './node_modules/whatwg-fetch/fetch.js', + 'page': './node_modules/page/page.js', + 'NProgress': './node_modules/nprogress/nprogress.js', + 'velocity': './node_modules/velocity-animate/velocity.js', + 'chart.js': './node_modules/chart.js/src/chart.js', + 'textarea-caret-position': './node_modules/textarea-caret/index.js', + 'misskey-text': './src/common/text/index.js', + 'strength.js': './node_modules/syuilo-password-strength/strength.js', + 'cropper': './node_modules/cropperjs/dist/cropper.js', + 'Sortable': './node_modules/sortablejs/Sortable.js', + 'fuck-adblock': './node_modules/fuckadblock/fuckadblock.js', + 'reconnecting-websocket': './node_modules/reconnecting-websocket/dist/index.js' + }, + appliesTo: { + 'includeExtensions': ['.js', '.ls'] + } +}; + +gulp.task('build:client', [ + 'build:ts', 'build:js', + 'build:client:scripts', + 'build:client:styles', + 'build:client:pug', + 'copy:client' +], () => { + gutil.log('ビルドが終了しました。'); + + if (isDebug) { + gutil.log('■ 注意! 開発モードでのビルドです。'); + } +}); + +gulp.task('build:client:scripts', done => { + gutil.log('スクリプトを構築します...'); + + // Get commit info + git.getLastCommit((err, commit) => { + glob('./src/web/app/*/script.js', (err, files) => { + const tasks = files.map(entry => { + let bundle = + browserify({ + entries: [entry] + }) + .transform(ls) + .transform(aliasify, aliasifyConfig) + + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + console.log(file); + return source; + })) + + // tagの{}の''を不要にする (その代わりスタイルの記法は使えなくなるけど) + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + const html = tag.sections.filter(s => s.name == 'html')[0]; + + html.lines = html.lines.map(line => { + if (line.replace(/\t/g, '')[0] === '|') { + return line; + } else { + return line.replace(/([+=])\s?\{(.+?)\}/g, '$1"{$2}"'); + } + }); + + const styles = tag.sections.filter(s => s.name == 'style'); + + if (styles.length == 0) { + return tag.compile(); + } + + styles.forEach(style => { + let head = style.lines.shift(); + head = head.replace(/([+=])\s?\{(.+?)\}/g, '$1"{$2}"'); + style.lines.unshift(head); + }); + + return tag.compile(); + })) + + // tagの@hogeをref='hoge'にする + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + const html = tag.sections.filter(s => s.name == 'html')[0]; + + html.lines = html.lines.map(line => { + if (line.indexOf('@') === -1) { + return line; + } else if (line.replace(/\t/g, '')[0] === '|') { + return line; + } else { + while (line.match(/[^\s']@[a-z-]+/) !== null) { + const match = line.match(/@[a-z-]+/); + let name = match[0]; + if (line[line.indexOf(name) + name.length] === '(') { + line = line.replace(name + '(', '(ref=\'' + camelCase(name.substr(1)) + '\','); + } else { + line = line.replace(name, '(ref=\'' + camelCase(name.substr(1)) + '\')'); + } + } + return line; + } + }); + + return tag.compile(); + + function camelCase(str): string { + return str.replace(/-([^\s])/g, (match, group1) => { + return group1.toUpperCase(); + }); + } + })) + + // tagのchain-caseをcamelCaseにする + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + const html = tag.sections.filter(s => s.name == 'html')[0]; + + html.lines = html.lines.map(line => { + (line.match(/\{.+?\}/g) || []).forEach(x => { + line = line.replace(x, camelCase(x)); + }); + return line; + }); + + return tag.compile(); + + function camelCase(str): string { + str = str.replace(/([a-z\-]+):/g, (match, group1) => { + return group1.replace(/\-/g, '###') + ':'; + }); + str = str.replace(/'(.+?)'/g, (match, group1) => { + return "'" + group1.replace(/\-/g, '###') + "'"; + }); + str = str.replace(/-([^\s0-9])/g, (match, group1) => { + return group1.toUpperCase(); + }); + str = str.replace(/###/g, '-'); + + return str; + } + })) + + // tagのstyleの属性 + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + + const styles = tag.sections.filter(s => s.name == 'style'); + + if (styles.length == 0) { + return tag.compile(); + } + + styles.forEach(style => { + let head = style.lines.shift(); + if (style.attr) { + style.attr = style.attr + ', type=\'stylus\', scoped'; + } else { + style.attr = 'type=\'stylus\', scoped'; + } + style.lines.unshift(head); + }); + + return tag.compile(); + })) + + // tagのstyleの定数 + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + + const styles = tag.sections.filter(s => s.name == 'style'); + + if (styles.length == 0) { + return tag.compile(); + } + + styles.forEach(style => { + const head = style.lines.shift(); + style.lines.unshift('$theme-color = ' + config.themeColor); + style.lines.unshift('$theme-color-foreground = #fff'); + style.lines.unshift(head); + }); + + return tag.compile(); + })) + + // tagのstyleを暗黙的に:scopeにする + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + + const styles = tag.sections.filter(s => s.name == 'style'); + + if (styles.length == 0) { + return tag.compile(); + } + + styles.forEach((style, i) => { + if (i != 0) { + return; + } + const head = style.lines.shift(); + style.lines = style.lines.map(line => { + return '\t' + line; + }); + style.lines.unshift(':scope'); + style.lines.unshift(head); + }); + + return tag.compile(); + })) + + // tagのtheme styleのパース + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + + const tag = new Tag(source); + + const styles = tag.sections.filter(s => s.name == 'style'); + + if (styles.length == 0) { + return tag.compile(); + } + + styles.forEach((style, i) => { + if (i == 0) { + return; + } else if (style.attr.substr(0, 6) != 'theme=') { + return; + } + const head = style.lines.shift(); + style.lines = style.lines.map(line => { + return '\t' + line; + }); + style.lines.unshift(':scope'); + style.lines = style.lines.map(line => { + return '\t' + line; + }); + style.lines.unshift('html[data-' + style.attr.match(/theme='(.+?)'/)[0] + ']'); + style.lines.unshift(head); + }); + + return tag.compile(); + })) + + // tagのstyleおよびscriptのインデントを不要にする + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + const tag = new Tag(source); + + tag.sections = tag.sections.map(section => { + if (section.name != 'html') { + section.indent++; + } + return section; + }); + + return tag.compile(); + })) + + // スペースでインデントされてないとエラーが出る + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + return source.replace(/\t/g, ' '); + })) + + .transform(transformify((source, file) => { + return source + .replace(/VERSION/g, `'${commit ? commit.hash : 'null'}'`) + .replace(/CONFIG\.theme-color/g, `'${config.themeColor}'`) + .replace(/CONFIG\.themeColor/g, `'${config.themeColor}'`) + .replace(/CONFIG\.api\.url/g, `'${config.scheme}://api.${config.host}'`) + .replace(/CONFIG\.urls\.about/g, `'${config.scheme}://about.${config.host}'`) + .replace(/CONFIG\.urls\.dev/g, `'${config.scheme}://dev.${config.host}'`) + .replace(/CONFIG\.url/g, `'${config.url}'`) + .replace(/CONFIG\.host/g, `'${config.host}'`) + .replace(/CONFIG\.recaptcha\.siteKey/g, `'${config.recaptcha.siteKey}'`) + ; + })) + + .transform(riotify, { + template: 'pug', + type: 'livescript', + expr: false, + compact: true, + parserOptions: { + style: { + compress: true, + rawDefine: config + } + } + }) + // Riotが謎の空白を挿入する + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + return source.replace(/\s<mk\-ellipsis>/g, '<mk-ellipsis>'); + })) + /* + // LiveScruptがHTMLクラスのショートカットを変な風に生成するのでそれを修正 + .transform(transformify((source, file) => { + if (file.substr(-4) !== '.tag') return source; + return source.replace(/class="\{\(\{(.+?)\}\)\}"/g, 'class="{$1}"'); + }))*/ + .bundle() + .pipe(source(entry.replace('./src/web/app/', './').replace('.ls', '.js'))); + + if (isProduction) { + bundle = bundle + .pipe(buffer()) + // ↓ https://github.com/mishoo/UglifyJS2/issues/448 + .pipe(babel({ + presets: ['es2015'] + })) + .pipe(uglify({ + compress: true + })); + } + + return bundle + .pipe(gulp.dest('./built/web/resources/')); + }); + + es.merge(tasks).on('end', done); + }); + }); +}); + +gulp.task('build:client:styles', () => { + gutil.log('フロントサイドスタイルを構築します...'); + + return gulp.src('./src/web/app/**/*.styl') + .pipe(stylus({ + 'include css': true, + compress: true, + rawDefine: config + })) + .pipe(isProduction + ? cssnano({ + safe: true // 高度な圧縮は無効にする (一部デザインが不適切になる場合があるため) + }) + : gutil.noop()) + .pipe(gulp.dest('./built/web/resources/')); +}); + +gulp.task('copy:client', [ + 'build:client:scripts', + 'build:client:styles' +], () => { + gutil.log('必要なリソースをコピーします...'); + + return es.merge( + gulp.src('./resources/**/*').pipe(gulp.dest('./built/web/resources/')), + gulp.src('./src/web/app/desktop/resources/**/*').pipe(gulp.dest('./built/web/resources/desktop/')), + gulp.src('./src/web/app/mobile/resources/**/*').pipe(gulp.dest('./built/web/resources/mobile/')), + gulp.src('./src/web/app/dev/resources/**/*').pipe(gulp.dest('./built/web/resources/dev/')), + gulp.src('./src/web/app/auth/resources/**/*').pipe(gulp.dest('./built/web/resources/auth/')) + ); +}); + +gulp.task('build:client:pug', [ + 'copy:client', + 'build:client:scripts', + 'build:client:styles' +], () => { + gutil.log('Pugをコンパイルします...'); + + return gulp.src([ + './src/web/app/*/view.pug' + ]) + .pipe(pug({ + locals: { + themeColor: config.themeColor + } + })) + .pipe(gulp.dest('./built/web/app/')); +}); + +class Tag { + sections: { + name: string; + attr?: string; + indent: number; + lines: string[]; + }[]; + + constructor(source) { + this.sections = []; + + source = source + .replace(/\r\n/g, '\n') + .replace(/\n(\t+?)\n/g, '\n') + .replace(/\n+/g, '\n'); + + const html = { + name: 'html', + indent: 0, + lines: [] + }; + + let flag = false; + source.split('\n').forEach((line, i) => { + const indent = line.lastIndexOf('\t') + 1; + if (i != 0 && indent == 0) { + flag = true; + } + if (!flag) { + source = source.replace(/^.*?\n/, ''); + html.lines.push(i == 0 ? line : line.substr(1)); + } + }); + + this.sections.push(html); + + while (source != '') { + const line = source.substr(0, source.indexOf('\n')); + const root = line.match(/^\t*([a-z]+)(\.|\()?/)[1]; + const beginIndent = line.lastIndexOf('\t') + 1; + flag = false; + const section = { + name: root, + attr: (line.match(/\((.+?)\)/) || [null, null])[1], + indent: beginIndent, + lines: [] + }; + source.split('\n').forEach((line, i) => { + const currentIndent = line.lastIndexOf('\t') + 1; + if (i != 0 && (currentIndent == beginIndent || currentIndent == 0)) { + flag = true; + } + if (!flag) { + if (i == 0 && line[line.length - 1] == '.') { + line = line.substr(0, line.length - 1); + } + if (i == 0 && line.indexOf('(') != -1) { + line = line.substr(0, line.indexOf('(')); + } + source = source.replace(/^.*?\n/, ''); + section.lines.push(i == 0 ? line.substr(beginIndent) : line.substr(beginIndent + 1)); + } + }); + this.sections.push(section); + } + } + + compile(): string { + let dist = ''; + this.sections.forEach((section, j) => { + dist += section.lines.map((line, i) => { + if (i == 0) { + const attr = section.attr != null ? '(' + section.attr + ')' : ''; + const tail = j != 0 ? '.' : ''; + return '\t'.repeat(section.indent) + line + attr + tail; + } else { + return '\t'.repeat(section.indent + 1) + line; + } + }).join('\n') + '\n'; + }); + return dist; + } +} |