diff --git a/gulpfile.ts b/gulpfile.ts index 85e594240..331d7d681 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -10,24 +10,22 @@ 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'; +import * as Webpack from 'webpack'; +import * as webpack from 'webpack-stream'; import stylus = require('gulp-stylus'); import cssnano = require('gulp-cssnano'); import * as uglify from 'gulp-uglify'; import riotify = require('riotify'); -import transformify = require('syuilo-transformify'); import pug = require('gulp-pug'); import git = require('git-last-commit'); import * as rimraf from 'rimraf'; import * as escapeHtml from 'escape-html'; import prominence = require('prominence'); -import promiseify = require('promiseify'); import * as chalk from 'chalk'; import imagemin = require('gulp-imagemin'); import * as rename from 'gulp-rename'; +import named = require('vinyl-named'); const env = process.env.NODE_ENV; const isProduction = env === 'production'; @@ -43,8 +41,8 @@ if (!fs.existsSync('./.config/default.yml')) { } (global as any).MISSKEY_CONFIG_PATH = '.config/default.yml'; -import { IConfig } from './src/config'; -const config = eval(require('typescript').transpile(require('fs').readFileSync('./src/config.ts').toString()))() as IConfig; +import { Config } from './src/config'; +const config = eval(require('typescript').transpile(require('fs').readFileSync('./src/config.ts').toString()))() as Config; const tsProject = ts.createProject('tsconfig.json'); @@ -150,7 +148,6 @@ gulp.task('default', ['build']); gulp.task('build:client', [ 'build:ts', 'build:js', 'build:client:scripts', - 'build:client:styles', 'build:client:pug', 'copy:client' ]); @@ -159,69 +156,99 @@ gulp.task('build:client:scripts', () => new Promise(async (ok) => { // Get commit info const commit = await prominence(git).getLastCommit(); - // Get all app scripts - const files = await promiseify(glob)('./src/web/app/*/script.js'); + const StringReplacePlugin = require('string-replace-webpack-plugin'); - // Compile for each scripts - const tasks = files.map(entry => { - let bundle = - browserify({ - entries: [entry] - }) - .transform(transformify((source, file) => { - return source - .replace(/VERSION/g, `'${commit ? commit.hash : 'null'}'`) - .replace(/\$theme\-color\-foreground/g, '#fff') - .replace(/\$theme\-color/g, config.themeColor) - .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, { - type: 'livescript', - expr: false, - compact: true, - parserOptions: { - style: { - compress: true, - rawDefine: config + /* webpack options */ + const pack = { + entry: { + 'client': './src/web/app/client/script.js', + 'desktop': './src/web/app/desktop/script.js', + 'mobile': './src/web/app/mobile/script.js', + 'dev': './src/web/app/dev/script.js', + 'auth': './src/web/app/auth/script.js' + }, + module: { + preLoaders: [ + { + test: /\.(tag|js|styl)$/, + exclude: /node_modules/, + loader: StringReplacePlugin.replace({ + replacements: [ + { pattern: /VERSION/g, replacement: () => `'${commit ? commit.hash : 'null'}'` }, + { pattern: /\$theme\-color\-foreground/g, replacement: () => '#fff' }, + { pattern: /\$theme\-color/g, replacement: () => config.themeColor }, + { pattern: /CONFIG\.theme\-color/g, replacement: () => `'${config.themeColor}'` }, + { pattern: /CONFIG\.themeColor/g, replacement: () => `'${config.themeColor}'` }, + { pattern: /CONFIG\.api\.url/g, replacement: () => `'${config.api_url}'` }, + { pattern: /CONFIG\.urls\.about/g, replacement: () => `'${config.about_url}'` }, + { pattern: /CONFIG\.urls\.dev/g, replacement: () => `'${config.dev_url}'` }, + { pattern: /CONFIG\.url/g, replacement: () => `'${config.url}'` }, + { pattern: /CONFIG\.host/g, replacement: () => `'${config.host}'` }, + { pattern: /CONFIG\.recaptcha\.siteKey/g, replacement: () => `'${config.recaptcha.siteKey}'` }, + ]}) + }, + ], + loaders: [ + { + test: /\.tag$/, + exclude: /node_modules/, + loader: 'riot-tag-loader', + query: { + hot: false, + type: 'livescript', + style: 'stylus', + expr: false, + compact: true, + parserOptions: { + style: { + compress: true + } + } } + }, + { + test: /\.styl$/, + loaders: ['style', 'css', 'stylus'] } - }) - .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 - })); + ] + }, + plugins: [ + new StringReplacePlugin() + ], + output: { + filename: '[name]/script.js' } + }; - return bundle - .pipe(gulp.dest('./built/web/resources/')); - }); + if (isProduction) { + // TODO. + // see https://github.com/webpack/webpack/issues/2545 + //pack.plugins.push(new Webpack.optimize.UglifyJsPlugin()) + } - es.merge(tasks).on('end', ok); + let stream = webpack(pack); + + // TODO: remove this block + if (isProduction) { + stream = stream + // ↓ https://github.com/mishoo/UglifyJS2/issues/448 + .pipe(babel({ + presets: ['es2015'] + })) + .pipe(uglify({ + compress: true + })); + } + + stream.pipe(gulp.dest('./built/web/resources/')); + + ok(); })); gulp.task('build:client:styles', () => - gulp.src('./src/web/app/**/*.styl') + gulp.src('./src/web/app/init.styl') .pipe(stylus({ - 'include css': true, - compress: true, - rawDefine: config + compress: true })) .pipe(isProduction ? cssnano({ @@ -232,8 +259,7 @@ gulp.task('build:client:styles', () => ); gulp.task('copy:client', [ - 'build:client:scripts', - 'build:client:styles' + 'build:client:scripts' ], () => gulp.src([ './resources/**/*', diff --git a/package.json b/package.json index 4004d0a81..92aa2f95a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "dependencies": { "@types/bcryptjs": "2.4.0", "@types/body-parser": "0.0.34", - "@types/browserify": "12.0.30", "@types/chai": "3.4.35", "@types/chai-http": "0.0.30", "@types/chalk": "0.4.31", @@ -62,8 +61,8 @@ "@types/serve-favicon": "2.2.28", "@types/twitter": "0.0.28", "@types/uuid": "2.0.29", - "@types/vinyl-buffer": "0.0.28", - "@types/vinyl-source-stream": "0.0.28", + "@types/webpack": "2.2.6", + "@types/webpack-stream": "3.2.6", "@types/websocket": "0.0.33", "autwh": "0.0.1", "babel-core": "6.23.1", @@ -72,7 +71,6 @@ "babel-preset-stage-3": "6.17.0", "bcryptjs": "2.4.3", "body-parser": "1.16.1", - "browserify": "14.1.0", "chai": "3.5.0", "chai-http": "3.0.0", "chalk": "1.1.3", @@ -81,6 +79,7 @@ "cors": "2.8.1", "cropperjs": "1.0.0-beta", "crypto": "0.0.3", + "css-loader": "0.26.1", "debug": "2.6.1", "deepcopy": "0.6.3", "download": "5.0.3", @@ -123,7 +122,6 @@ "nyaize": "0.0.2", "page": "1.7.1", "prominence": "0.2.0", - "promiseify": "0.2.0", "pug": "2.0.0-beta9", "ratelimiter": "2.2.0", "recaptcha-promise": "0.1.2", @@ -133,15 +131,17 @@ "rimraf": "2.6.0", "riot": "3.3.0", "riot-compiler": "3.2.0", + "riot-tag-loader": "1.0.0", "riotify": "2.0.0", "rndstr": "1.0.0", "s-age": "1.1.0", "serve-favicon": "2.3.2", + "string-replace-webpack-plugin": "0.0.5", + "stylus-loader": "^2.5.0", "subdomain": "1.2.0", "summaly": "2.0.0", "swagger-jsdoc": "1.9.1", "syuilo-password-strength": "0.0.1", - "syuilo-transformify": "0.1.2", "tcp-port-used": "0.1.2", "textarea-caret": "3.0.2", "tslint": "4.4.2", @@ -149,8 +149,9 @@ "uuid": "3.0.1", "velocity-animate": "1.4.2", "vhost": "3.0.2", - "vinyl-buffer": "1.0.0", - "vinyl-source-stream": "1.1.0", + "vinyl-named": "1.1.0", + "webpack": "2.2.1", + "webpack-stream": "3.2.0", "websocket": "1.0.24", "whatwg-fetch": "2.0.2", "xml2json": "0.11.0" diff --git a/src/config.ts b/src/config.ts index 54ed0826a..64a6b7c9b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -79,10 +79,13 @@ interface Mixin { secondary_scheme: string; api_url: string; auth_url: string; + about_url: string; dev_url: string; drive_url: string; } +export type Config = Source & Mixin; + export default function load() { const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')) as Source; @@ -104,6 +107,7 @@ export default function load() { mixin.api_url = `${mixin.scheme}://api.${mixin.host}`; mixin.auth_url = `${mixin.scheme}://auth.${mixin.host}`; mixin.dev_url = `${mixin.scheme}://dev.${mixin.host}`; + mixin.about_url = `${mixin.scheme}://about.${mixin.host}`; mixin.drive_url = `${mixin.secondary_scheme}://file.${mixin.secondary_host}`; return Object.assign(config, mixin); diff --git a/src/web/app/auth/script.js b/src/web/app/auth/script.js index c0065c85f..8c9138efa 100644 --- a/src/web/app/auth/script.js +++ b/src/web/app/auth/script.js @@ -2,6 +2,10 @@ * Authorize Form */ +// Style +//import './style.styl'; +require('./style.styl'); + const riot = require('riot'); document.title = 'Misskey | アプリの連携'; require('./tags'); diff --git a/src/web/app/base.styl b/src/web/app/base.styl index c35f66c9a..5a1da3813 100644 --- a/src/web/app/base.styl +++ b/src/web/app/base.styl @@ -1,7 +1,7 @@ @charset 'utf-8' -$theme-color = convert(themeColor) -$theme-color-foreground = convert(themeColorForeground) +$theme-color = #f76d6c +$theme-color-foreground = #fff @import './reset' diff --git a/src/web/app/client/script.js b/src/web/app/client/script.js index d8531e9cc..880500eb1 100644 --- a/src/web/app/client/script.js +++ b/src/web/app/client/script.js @@ -2,18 +2,9 @@ const head = document.getElementsByTagName('head')[0]; const ua = navigator.userAgent.toLowerCase(); const isMobile = /mobile|iphone|ipad|android/.test(ua); -if (isMobile) { - mountMobile(); -} else { - mountDesktop(); -} +isMobile ? mountMobile() : mountDesktop(); function mountDesktop() { - const style = document.createElement('link'); - style.setAttribute('href', '/_/resources/desktop/style.css'); - style.setAttribute('rel', 'stylesheet'); - head.appendChild(style); - const script = document.createElement('script'); script.setAttribute('src', '/_/resources/desktop/script.js'); script.setAttribute('async', 'true'); @@ -27,11 +18,6 @@ function mountMobile() { meta.setAttribute('content', 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'); head.appendChild(meta); - const style = document.createElement('link'); - style.setAttribute('href', '/_/resources/mobile/style.css'); - style.setAttribute('rel', 'stylesheet'); - head.appendChild(style); - const script = document.createElement('script'); script.setAttribute('src', '/_/resources/mobile/script.js'); script.setAttribute('async', 'true'); diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js index b240e9ab9..3d25e9b1d 100644 --- a/src/web/app/desktop/script.js +++ b/src/web/app/desktop/script.js @@ -2,6 +2,10 @@ * Desktop Client */ +// Style +//import './style.styl'; +require('./style.styl'); + require('chart.js'); require('./tags'); const riot = require('riot'); diff --git a/src/web/app/dev/script.js b/src/web/app/dev/script.js index e0644c263..94b17c85e 100644 --- a/src/web/app/dev/script.js +++ b/src/web/app/dev/script.js @@ -2,6 +2,10 @@ * Developer Center */ +// Style +//import './style.styl'; +require('./style.styl'); + require('./tags'); const boot = require('../boot'); const route = require('./router'); diff --git a/src/web/app/dev/style.styl b/src/web/app/dev/style.styl index a7e51b894..4fd537709 100644 --- a/src/web/app/dev/style.styl +++ b/src/web/app/dev/style.styl @@ -2,9 +2,3 @@ html background-color #fff - -#init - background #100f0f - - > p - color $theme-color diff --git a/src/web/app/mobile/script.js b/src/web/app/mobile/script.js index 6f732da9c..034010b62 100644 --- a/src/web/app/mobile/script.js +++ b/src/web/app/mobile/script.js @@ -2,6 +2,10 @@ * Mobile Client */ +// Style +//import './style.styl'; +require('./style.styl'); + require('./tags'); const boot = require('../boot'); const mixins = require('./mixins');