diff --git a/package.json b/package.json
index bf924dcdb0a2ca1567cb0cb79d90d6a677f53f2a..06e517a0d1191f07c354123d871c75a7d471de77 100644
--- a/package.json
+++ b/package.json
@@ -157,6 +157,7 @@
 		"serve-favicon": "2.4.5",
 		"sortablejs": "1.7.0",
 		"speakeasy": "2.0.0",
+		"string-replace-loader": "^1.3.0",
 		"string-replace-webpack-plugin": "0.1.3",
 		"style-loader": "0.20.1",
 		"stylus": "0.54.5",
@@ -182,6 +183,7 @@
 		"vue-template-compiler": "^2.5.13",
 		"web-push": "3.2.5",
 		"webpack": "3.10.0",
+		"webpack-replace-loader": "^1.3.0",
 		"websocket": "1.0.25",
 		"xev": "2.0.0"
 	}
diff --git a/webpack/module/rules/base64.ts b/webpack/module/rules/base64.ts
index 886f0e8b3870d2a5d267e1ca826b85513bae8e0c..c2f6b9339e9734fff2aacd4953f55674ce29e76b 100644
--- a/webpack/module/rules/base64.ts
+++ b/webpack/module/rules/base64.ts
@@ -8,13 +8,11 @@ export default () => ({
 	enforce: 'pre',
 	test: /\.(vue|js)$/,
 	exclude: /node_modules/,
-	use: [{
-		loader: 'replace-string-loader',
-		options: {
-			search: /%base64:(.+?)%/g,
-			replace: (_, key) => {
-				return fs.readFileSync(__dirname + '/../../../src/web/' + key, 'base64');
-			}
+	loader: 'string-replace-loader',
+	query: {
+		search: /%base64:(.+?)%/g,
+		replace: (_, key) => {
+			return fs.readFileSync(__dirname + '/../../../src/web/' + key, 'base64');
 		}
-	}]
+	}
 });
diff --git a/webpack/module/rules/collapse-spaces.ts b/webpack/module/rules/collapse-spaces.ts
index 48fd57f0172c18df4e630bdf62d28c906e8fa050..734c735926d834e2f7c2fcc2ca7f6069d22484ed 100644
--- a/webpack/module/rules/collapse-spaces.ts
+++ b/webpack/module/rules/collapse-spaces.ts
@@ -1,20 +1,19 @@
 import * as fs from 'fs';
 const minify = require('html-minifier').minify;
-const StringReplacePlugin = require('string-replace-webpack-plugin');
 
 export default () => ({
 	enforce: 'pre',
 	test: /\.vue$/,
 	exclude: /node_modules/,
-	loader: StringReplacePlugin.replace({
-		replacements: [{
-			pattern: /^<template>([\s\S]+?)\r?\n<\/template>/, replacement: html => {
-				return minify(html, {
-					collapseWhitespace: true,
-					collapseInlineTagWhitespace: true,
-					keepClosingSlash: true
-				});
-			}
-		}]
-	})
+	loader: 'string-replace-loader',
+	query: {
+		search: /^<template>([\s\S]+?)\r?\n<\/template>/,
+		replace: html => {
+			return minify(html, {
+				collapseWhitespace: true,
+				collapseInlineTagWhitespace: true,
+				keepClosingSlash: true
+			});
+		}
+	}
 });
diff --git a/webpack/module/rules/fa.ts b/webpack/module/rules/fa.ts
index 56ca19d4b4d116be25657ff5764d017b92f9b13b..2ac89ce4f87847daead4fefbf2a83549b55b02af 100644
--- a/webpack/module/rules/fa.ts
+++ b/webpack/module/rules/fa.ts
@@ -8,11 +8,9 @@ export default () => ({
 	enforce: 'pre',
 	test: /\.(vue|js|ts)$/,
 	exclude: /node_modules/,
-	use: [{
-		loader: 'replace-string-loader',
-		options: {
-			search: pattern,
-			replace: replacement
-		}
-	}]
+	loader: 'string-replace-loader',
+	query: {
+		search: pattern,
+		replace: replacement
+	}
 });
diff --git a/webpack/module/rules/i18n.ts b/webpack/module/rules/i18n.ts
index 1bd771f43ba3abf956387dd21d8d7d9b7cbf89ee..2352a42be0ae9c800c4626ca6656db17a40b007d 100644
--- a/webpack/module/rules/i18n.ts
+++ b/webpack/module/rules/i18n.ts
@@ -11,12 +11,10 @@ export default lang => {
 		enforce: 'pre',
 		test: /\.(vue|js|ts)$/,
 		exclude: /node_modules/,
-		use: [{
-			loader: 'replace-string-loader',
-			options: {
-				search: replacer.pattern,
-				replace: replacer.replacement
-			}
-		}]
+		loader: 'string-replace-loader',
+		query: {
+			search: replacer.pattern,
+			replace: replacer.replacement
+		}
 	};
 };
diff --git a/webpack/module/rules/index.ts b/webpack/module/rules/index.ts
index c4442b06cd196770ae632d637a161e431d2b1bbf..1ddebacffa451d766507e9f3e1acb81ab639e7e5 100644
--- a/webpack/module/rules/index.ts
+++ b/webpack/module/rules/index.ts
@@ -1,18 +1,16 @@
 import i18n from './i18n';
 import fa from './fa';
 //import base64 from './base64';
-import themeColor from './theme-color';
 import vue from './vue';
 import stylus from './stylus';
 import typescript from './typescript';
 import collapseSpaces from './collapse-spaces';
 
 export default lang => [
-	collapseSpaces(),
-	i18n(lang),
-	fa(),
+	//collapseSpaces(),
+	//i18n(lang),
+	//fa(),
 	//base64(),
-	themeColor(),
 	vue(),
 	stylus(),
 	typescript()
diff --git a/webpack/module/rules/theme-color.ts b/webpack/module/rules/theme-color.ts
deleted file mode 100644
index 4828e00ec775acbbc1d6c3b3d78063fee96e8839..0000000000000000000000000000000000000000
--- a/webpack/module/rules/theme-color.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Theme color provider
- */
-
-const constants = require('../../../src/const.json');
-
-export default () => ({
-	enforce: 'pre',
-	test: /\.vue$/,
-	exclude: /node_modules/,
-	use: [{
-		loader: 'replace-string-loader',
-		options: {
-			search: '$theme-color-foreground',
-			replace: constants.themeColorForeground,
-			flags: 'g'
-		}
-	}, {
-		loader: 'replace-string-loader',
-		options: {
-			search: '$theme-color',
-			replace: constants.themeColor,
-			flags: 'g'
-		}
-	}]
-});
diff --git a/webpack/module/rules/vue.ts b/webpack/module/rules/vue.ts
index 02d644615cf06eecfeedff58e4250566da2bfd14..990f839915e1479b326c5b00df28a102c651c5de 100644
--- a/webpack/module/rules/vue.ts
+++ b/webpack/module/rules/vue.ts
@@ -2,12 +2,30 @@
  * Vue
  */
 
+const constants = require('../../../src/const.json');
+
 export default () => ({
 	test: /\.vue$/,
 	exclude: /node_modules/,
-	loader: 'vue-loader',
-	options: {
-		cssSourceMap: false,
-		preserveWhitespace: false
-	}
+	use: [{
+		loader: 'vue-loader',
+		options: {
+			cssSourceMap: false,
+			preserveWhitespace: false
+		}
+	}, {
+		loader: 'webpack-replace-loader',
+		options: {
+			search: '$theme-color',
+			replace: constants.themeColor,
+			attr: 'g'
+		}
+	}, {
+		loader: 'webpack-replace-loader',
+		query: {
+			search: '$theme-color-foreground',
+			replace: constants.themeColorForeground,
+			attr: 'g'
+		}
+	}]
 });