diff --git a/gulpfile.ts b/gulpfile.ts index 5518d0e26..9ba88125f 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -19,13 +19,16 @@ import * as chalk from 'chalk'; import imagemin = require('gulp-imagemin'); import * as rename from 'gulp-rename'; import * as mocha from 'gulp-mocha'; +import * as replace from 'gulp-replace'; +import getVersion from './src/version'; const env = process.env.NODE_ENV; const isProduction = env === 'production'; const isDebug = !isProduction; if (isDebug) { - console.log(chalk.yellow.bold('!!!WARNING!!! NODE_ENV is not "production". (built script compessing will not be performed.)')); + console.warn(chalk.yellow.bold('WARNING! NODE_ENV is not "production".')); + console.warn(chalk.yellow.bold(' built script compessing will not be performed.')); } const constants = require('./src/const.json'); @@ -126,15 +129,21 @@ gulp.task('build:client', [ 'copy:client' ]); -gulp.task('build:client:scripts', () => - es.merge( - webpack(require('./webpack.config'), require('webpack')) - .pipe(gulp.dest('./built/web/resources/')) as any, - gulp.src('./src/web/app/client/script.js') - //.pipe(isProduction ? uglify() : gutil.noop()) - .pipe(gulp.dest('./built/web/resources/client/')) as any - ) -); +gulp.task('build:client:scripts', done => { + getVersion.then(version => { + require('./webpack.config').then(webpackOptions => { + es.merge( + webpack(webpackOptions, require('webpack')) + .pipe(gulp.dest('./built/web/resources/')) as any, + gulp.src('./src/web/app/client/script.js') + .pipe(replace('VERSION', JSON.stringify(version))) + //.pipe(isProduction ? uglify() : gutil.noop()) + .pipe(gulp.dest('./built/web/resources/client/')) as any + ); + done(); + }); + }); +}); gulp.task('build:client:styles', () => gulp.src('./src/web/app/init.css') @@ -163,12 +172,16 @@ gulp.task('build:client:pug', [ 'copy:client', 'build:client:scripts', 'build:client:styles' -], () => +], done => { + getVersion.then(version => { gulp.src('./src/web/app/*/view.pug') .pipe(pug({ locals: { + version: version, themeColor: constants.themeColor } })) - .pipe(gulp.dest('./built/web/app/')) -); + .pipe(gulp.dest('./built/web/app/')); + done(); + }); +}); diff --git a/package.json b/package.json index 49ffc1bab..52f9c9551 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,14 @@ "@types/express": "4.0.35", "@types/glob": "5.0.30", "@types/gm": "1.17.30", - "@types/gulp-tslint": "3.6.31", - "@types/gulp-rename": "0.0.32", - "@types/gulp-mocha": "0.0.30", "@types/gulp": "4.0.2", + "@types/gulp-mocha": "0.0.30", + "@types/gulp-rename": "0.0.32", + "@types/gulp-replace": "0.0.30", + "@types/gulp-tslint": "3.6.31", "@types/gulp-typescript": "0.0.32", - "@types/gulp-util": "3.0.31", "@types/gulp-uglify": "0.0.30", + "@types/gulp-util": "3.0.31", "@types/inquirer": "0.0.32", "@types/is-root": "1.0.0", "@types/is-url": "1.2.28", @@ -65,8 +66,8 @@ "@types/websocket": "0.0.33", "autwh": "0.0.1", "bcryptjs": "2.4.3", - "cafy": "2.3.0", "body-parser": "1.17.1", + "cafy": "2.3.0", "chai": "3.5.0", "chai-http": "3.0.0", "chalk": "1.1.3", @@ -95,6 +96,7 @@ "gulp-mocha": "4.1.0", "gulp-pug": "3.3.0", "gulp-rename": "1.2.2", + "gulp-replace": "0.5.4", "gulp-tslint": "7.1.0", "gulp-typescript": "3.1.6", "gulp-uglify": "2.1.0", diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts index ea2a8206a..0cbeaae82 100644 --- a/src/api/endpoints/meta.ts +++ b/src/api/endpoints/meta.ts @@ -2,7 +2,7 @@ * Module dependencies */ import prominence from 'prominence'; -import git from 'git-last-commit'; +import getVersion from '../../version'; import config from '../../conf'; /** @@ -39,11 +39,11 @@ import config from '../../conf'; * @return {Promise} */ module.exports = (params) => new Promise(async (res, rej) => { - const commit = await prominence(git).getLastCommit(); + const version = await getVersion.then(); res({ maintainer: config.maintainer, - commit: commit.shortHash, + version: version, secure: config.https.enable }); }); diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 000000000..1022ab83b --- /dev/null +++ b/src/version.ts @@ -0,0 +1,12 @@ +import prominence = require('prominence'); +import git = require('git-last-commit'); + +const getVersion = new Promise(async resolve => { + const commit = await prominence(git).getLastCommit(); + + const version = commit.shortHash; + + resolve(version); +}); + +export default getVersion; diff --git a/src/web/app/auth/view.pug b/src/web/app/auth/view.pug index 94ec12a9a..03ec4e6f7 100644 --- a/src/web/app/auth/view.pug +++ b/src/web/app/auth/view.pug @@ -1,6 +1,5 @@ extends ../base block head - meta(name='viewport', content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no') - link(rel='stylesheet', href='/resources/auth/style.css') - script(src='/resources/auth/script.js', async, defer) + meta(name='viewport' content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no') + script(src=`/resources/auth/script.${version}.js` async defer) diff --git a/src/web/app/base.pug b/src/web/app/base.pug index e65726fba..90fb7b24a 100644 --- a/src/web/app/base.pug +++ b/src/web/app/base.pug @@ -2,17 +2,17 @@ doctype html != '\r\n\r\n' -html(lang='ja', dir='ltr') +html(lang='ja' dir='ltr') head meta(charset='utf-8') - meta(name='application-name', content='Misskey') - meta(name='theme-color', content= themeColor) - meta(name='referrer', content='origin') + meta(name='application-name' content='Misskey') + meta(name='theme-color' content=themeColor) + meta(name='referrer' content='origin') title Misskey style include ./../../../built/web/resources/init.css - script(src='https://use.fontawesome.com/22aba0df4f.js', async) + script(src='https://use.fontawesome.com/22aba0df4f.js' async) block head body diff --git a/src/web/app/boot.js b/src/web/app/boot.js index 6741a4423..4d008ad66 100644 --- a/src/web/app/boot.js +++ b/src/web/app/boot.js @@ -49,6 +49,26 @@ try { Storage.prototype.setItem = () => { }; // noop } +// クライアントを更新すべきならする +if (localStorage.getItem('should-refresh') == 'true') { + localStorage.removeItem('should-refresh'); + location.reload(true); +} + +// 更新チェック +setTimeout(() => { + fetch(CONFIG.apiUrl + '/meta', { + method: 'POST' + }).then(res => { + res.json().then(meta => { + if (meta.version != VERSION) { + localStorage.setItem('should-refresh', 'true'); + alert('Misskeyの新しいバージョンがあります。ページを再度読み込みすると更新が適用されます。'); + } + }); + }); +}, 3000); + // ユーザーをフェッチしてコールバックする module.exports = callback => { // Get cached account data diff --git a/src/web/app/client/script.js b/src/web/app/client/script.js index dcd6bb16f..ffc9c892c 100644 --- a/src/web/app/client/script.js +++ b/src/web/app/client/script.js @@ -1,18 +1,29 @@ +/** + * MISSKEY ENTRY POINT + */ (() => { const head = document.getElementsByTagName('head')[0]; + + // Detect user agent const ua = navigator.userAgent.toLowerCase(); const isMobile = /mobile|iphone|ipad|android/.test(ua); isMobile ? mountMobile() : mountDesktop(); + /** + * Mount the desktop app + */ function mountDesktop() { const script = document.createElement('script'); - script.setAttribute('src', '/resources/desktop/script.js'); + script.setAttribute('src', `/resources/desktop/script.${VERSION}.js`); script.setAttribute('async', 'true'); script.setAttribute('defer', 'true'); head.appendChild(script); } + /** + * Mount the mobile app + */ function mountMobile() { const meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); @@ -20,7 +31,7 @@ head.appendChild(meta); const script = document.createElement('script'); - script.setAttribute('src', '/resources/mobile/script.js'); + script.setAttribute('src', `/resources/mobile/script.${VERSION}.js`); script.setAttribute('async', 'true'); script.setAttribute('defer', 'true'); head.appendChild(script); diff --git a/src/web/app/dev/view.pug b/src/web/app/dev/view.pug index 5b426bcbd..8d21c7e71 100644 --- a/src/web/app/dev/view.pug +++ b/src/web/app/dev/view.pug @@ -1,5 +1,4 @@ extends ../base block head - link(rel='stylesheet', href='/resources/dev/style.css') - script(src='/resources/dev/script.js', async, defer) + script(src=`/resources/dev/script.${version}.js` async defer) diff --git a/webpack.config.ts b/webpack.config.ts index 90e23d104..65aeda5ea 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -4,73 +4,79 @@ import * as webpack from 'webpack'; const StringReplacePlugin = require('string-replace-webpack-plugin'); +import getVersion from './src/version'; const constants = require('./src/const.json'); const env = process.env.NODE_ENV; const isProduction = env === 'production'; -const pack: webpack.Configuration = { - entry: { - '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: { - rules: [ - { - enforce: 'pre', - test: /\.tag$/, - exclude: /node_modules/, - loader: StringReplacePlugin.replace({ - replacements: [ - { pattern: /\$theme\-color\-foreground/g, replacement: () => constants.themeColorForeground }, - { pattern: /\$theme\-color/g, replacement: () => constants.themeColor }, - ] - }) - }, - { - test: /\.tag$/, - exclude: /node_modules/, - loader: 'riot-tag-loader', - query: { - hot: false, - style: 'stylus', - expr: false, - compact: true, - parserOptions: { - style: { - compress: true +module.exports = new Promise(async resolve => { + const version = await getVersion.then(); + + const pack: webpack.Configuration = { + entry: { + '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: { + rules: [ + { + enforce: 'pre', + test: /\.tag$/, + exclude: /node_modules/, + loader: StringReplacePlugin.replace({ + replacements: [ + { pattern: /\$theme\-color\-foreground/g, replacement: () => constants.themeColorForeground }, + { pattern: /\$theme\-color/g, replacement: () => constants.themeColor }, + ] + }) + }, + { + test: /\.tag$/, + exclude: /node_modules/, + loader: 'riot-tag-loader', + query: { + hot: false, + style: 'stylus', + expr: false, + compact: true, + parserOptions: { + style: { + compress: true + } } } + }, + { + test: /\.styl$/, + exclude: /node_modules/, + use: [ + { loader: 'style-loader' }, + { loader: 'css-loader' }, + { loader: 'stylus-loader' } + ] } - }, - { - test: /\.styl$/, - exclude: /node_modules/, - use: [ - { loader: 'style-loader' }, - { loader: 'css-loader' }, - { loader: 'stylus-loader' } - ] - } - ] - }, - plugins: [ - new webpack.DefinePlugin({ - CONFIG: { - themeColor: JSON.stringify(constants.themeColor) - } - }), - new StringReplacePlugin() - ], - output: { - filename: '[name]/script.js' + ] + }, + plugins: [ + new webpack.DefinePlugin({ + VERSION: JSON.stringify(version), + CONFIG: { + themeColor: JSON.stringify(constants.themeColor) + } + }), + new StringReplacePlugin() + ], + output: { + filename: `[name]/script.${version}.js` + } + }; + + if (isProduction) { + //pack.plugins.push(new webpack.optimize.UglifyJsPlugin()); } -}; -if (isProduction) { - //pack.plugins.push(new webpack.optimize.UglifyJsPlugin()); -} - -module.exports = pack; + resolve(pack); +});