Browse Source

chore: upgrade to babel@7 + webpack@4

Gerald 7 years ago
parent
commit
a83334c018
50 changed files with 1397 additions and 414 deletions
  1. 31 0
      .babelrc
  2. 1 0
      .browserslistrc
  3. 15 38
      .eslintrc.js
  4. 10 1
      gulpfile.js
  5. 39 27
      package.json
  6. 11 0
      scripts/eslint/vue.js
  7. 21 0
      scripts/template.html
  8. 51 0
      scripts/util.js
  9. 0 66
      scripts/utils.js
  10. 0 7
      scripts/vue-loader.conf.js
  11. 27 54
      scripts/webpack.base.conf.js
  12. 103 54
      scripts/webpack.conf.js
  13. 3 4
      scripts/webpack.test.conf.js
  14. 8 0
      scripts/webpack/analyze.js
  15. 73 0
      scripts/webpack/common.js
  16. 15 0
      scripts/webpack/raw.js
  17. 26 0
      scripts/webpack/svg.js
  18. 12 0
      scripts/webpack/sw.js
  19. 20 0
      scripts/webpack/url.js
  20. 13 0
      scripts/webpack/vue.js
  21. 0 18
      src/.babelrc
  22. 0 1
      src/background/index.js
  23. 4 4
      src/background/utils/index.js
  24. 1 1
      src/common/browser.js
  25. 1 1
      src/common/ui/icon.vue
  26. 0 13
      src/confirm/index.html
  27. 4 2
      src/confirm/index.js
  28. 0 1
      src/injected/index.js
  29. 0 14
      src/options/index.html
  30. 5 3
      src/options/index.js
  31. 1 1
      src/options/views/script-item.vue
  32. 2 2
      src/options/views/tab-installed.vue
  33. 0 13
      src/popup/index.html
  34. 4 2
      src/popup/index.js
  35. 0 0
      src/resources/svg/arrow.svg
  36. 0 0
      src/resources/svg/author.svg
  37. 0 0
      src/resources/svg/code.svg
  38. 0 0
      src/resources/svg/cog.svg
  39. 0 0
      src/resources/svg/filter.svg
  40. 0 0
      src/resources/svg/home.svg
  41. 0 0
      src/resources/svg/info.svg
  42. 0 0
      src/resources/svg/plus.svg
  43. 0 0
      src/resources/svg/question.svg
  44. 0 0
      src/resources/svg/refresh.svg
  45. 0 0
      src/resources/svg/search.svg
  46. 0 0
      src/resources/svg/toggle-off.svg
  47. 0 0
      src/resources/svg/toggle-on.svg
  48. 0 0
      src/resources/svg/trash.svg
  49. 0 0
      src/resources/svg/undo.svg
  50. 896 87
      yarn.lock

+ 31 - 0
.babelrc

@@ -0,0 +1,31 @@
+{
+  "presets": [
+    ["@babel/preset-env", {
+      "modules": false,
+      "useBuiltIns": "usage",
+    }],
+  ],
+  "plugins": [
+    "@babel/plugin-transform-runtime",
+
+    // stage-2
+    ["@babel/plugin-proposal-decorators", { "legacy": true }],
+    "@babel/plugin-proposal-function-sent",
+    "@babel/plugin-proposal-export-namespace-from",
+    "@babel/plugin-proposal-numeric-separator",
+    "@babel/plugin-proposal-throw-expressions",
+
+    // stage-3
+    "@babel/plugin-syntax-dynamic-import",
+    "@babel/plugin-syntax-import-meta",
+    ["@babel/plugin-proposal-class-properties", { "loose": false }],
+    "@babel/plugin-proposal-json-strings",
+
+    ["module-resolver", {
+      "alias": {
+        "#": "./src",
+      },
+      "extensions": [ ".js", ".vue" ],
+    }],
+  ],
+}

+ 1 - 0
.browserslistrc

@@ -0,0 +1 @@
+Chrome >= 45

+ 15 - 38
.eslintrc.js

@@ -1,58 +1,35 @@
-// http://eslint.org/docs/user-guide/configuring
-
 module.exports = {
-  root: true,
   parser: 'babel-eslint',
-  parserOptions: {
-    sourceType: 'module'
-  },
+  extends: [
+    'airbnb-base',
+    require.resolve('./scripts/eslint/vue'),
+  ],
   env: {
     browser: true,
   },
-  extends: 'airbnb-base',
-  // required to lint *.vue files
-  plugins: [
-    'html'
-  ],
-  // check if imports actually resolve
-  'settings': {
+  plugins: [],
+  settings: {
     'import/resolver': {
-      'webpack': {
-        'config': 'scripts/webpack.base.conf.js'
-      }
-    }
+      'babel-module': {},
+    },
   },
-  // add your custom rules here
-  'rules': {
-    // don't require .vue extension when importing
-    'import/extensions': ['error', 'always', {
-      'js': 'never',
-      'vue': 'never'
-    }],
-    // allow debugger during development
-    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
-    'no-console': ['warn', {
-      allow: ['error', 'warn', 'info'],
-    }],
-    'no-param-reassign': ['error', {
-      props: false,
-    }],
+  rules: {
+    'no-param-reassign': ['error', { props: false }],
     'consistent-return': 'off',
     'no-use-before-define': ['error', 'nofunc'],
-    'object-shorthand': ['error', 'always'],
     'no-mixed-operators': 'off',
     'no-bitwise': ['error', { int32Hint: true }],
-    'no-underscore-dangle': 'off',
     'arrow-parens': ['error', 'as-needed'],
     'prefer-promise-reject-errors': 'off',
     'prefer-destructuring': ['error', { array: false }],
-    indent: ['error', 2, {
-      MemberExpression: 0,
-      flatTernaryExpressions: true,
+    'no-console': ['warn', {
+      allow: ['error', 'warn', 'info'],
     }],
+    indent: ['error', 2, { MemberExpression: 0 }],
+    'object-shorthand': ['error', 'always'],
   },
   globals: {
     browser: true,
     zip: true,
   },
-}
+};

+ 10 - 1
gulpfile.js

@@ -7,9 +7,10 @@ const plumber = require('gulp-plumber');
 const yaml = require('js-yaml');
 const webpack = require('webpack');
 const webpackConfig = require('./scripts/webpack.conf');
+const webpackTestConfig = require('./scripts/webpack.test.conf');
 const i18n = require('./scripts/i18n');
 const string = require('./scripts/string');
-const { isProd } = require('./scripts/utils');
+const { isProd } = require('./scripts/util');
 const pkg = require('./package.json');
 
 const DIST = 'dist';
@@ -75,6 +76,13 @@ function jsProd(done) {
   });
 }
 
+function jsTest(done) {
+  webpack(webpackTestConfig, (...args) => {
+    webpackCallback(...args);
+    done();
+  });
+}
+
 function manifest() {
   return gulp.src(paths.manifest, { base: 'src' })
   .pipe(string((input, file) => {
@@ -150,5 +158,6 @@ const pack = gulp.parallel(manifest, copyFiles, copyI18n);
 exports.clean = clean;
 exports.dev = gulp.series(gulp.parallel(pack, jsDev), watch);
 exports.build = gulp.parallel(pack, jsProd);
+exports.buildTest = jsTest;
 exports.i18n = updateI18n;
 exports.check = checkI18n;

+ 39 - 27
package.json

@@ -6,59 +6,70 @@
     "prebuild": "yarn lint && gulp clean",
     "build": "cross-env NODE_ENV=production gulp build",
     "build:firefox": "cross-env TARGET=firefox yarn build",
-    "analyze": "webpack --profile --json --config scripts/webpack.conf.js | webpack-bundle-size-analyzer",
-    "analyze:json": "webpack --profile --json --config scripts/webpack.conf.js > stats.json",
+    "analyze": "cross-env RUN_ENV=analyze npm run build",
     "i18n": "gulp i18n",
     "lint": "yarn lint:js && yarn lint:yml",
     "lint:js": "eslint --ext .js,.vue .",
     "lint:yml": "gulp check",
     "svgo": "svgo --config .svgo.yml src/resources/icons",
-    "pretest": "cross-env NODE_ENV=test webpack --config scripts/webpack.test.conf.js",
+    "pretest": "cross-env NODE_ENV=test gulp buildTest",
     "test": "node dist/test",
     "transform": "node scripts/transform-lock"
   },
   "description": "Violentmonkey",
   "devDependencies": {
-    "babel-core": "^6.26.0",
-    "babel-eslint": "^8.2.2",
-    "babel-loader": "^7.1.2",
+    "@babel/core": "^7.1.2",
+    "@babel/plugin-proposal-class-properties": "^7.1.0",
+    "@babel/plugin-proposal-decorators": "^7.1.2",
+    "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
+    "@babel/plugin-proposal-function-sent": "^7.1.0",
+    "@babel/plugin-proposal-json-strings": "^7.0.0",
+    "@babel/plugin-proposal-numeric-separator": "^7.0.0",
+    "@babel/plugin-proposal-throw-expressions": "^7.0.0",
+    "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+    "@babel/plugin-syntax-import-meta": "^7.0.0",
+    "@babel/plugin-transform-runtime": "^7.1.0",
+    "@babel/preset-env": "^7.1.0",
+    "babel-eslint": "^10.0.1",
+    "babel-loader": "^8.0.4",
     "babel-minify-webpack-plugin": "^0.3.0",
-    "babel-plugin-transform-export-extensions": "^6.22.0",
-    "babel-plugin-transform-runtime": "^6.23.0",
-    "babel-preset-env": "^1.6.0",
+    "babel-plugin-module-resolver": "^3.1.1",
     "cross-env": "^5.0.5",
     "css-loader": "^1.0.0",
     "del": "^3.0.0",
-    "eslint": "^5.2.0",
-    "eslint-config-airbnb-base": "^13.0.0",
+    "eslint": "^5.7.0",
+    "eslint-config-airbnb-base": "^13.1.0",
+    "eslint-import-resolver-babel-module": "^5.0.0-beta.1",
     "eslint-import-resolver-webpack": "^0.10.1",
-    "eslint-plugin-html": "^4.0.1",
-    "eslint-plugin-import": "^2.9.0",
-    "extract-text-webpack-plugin": "^3.0.0",
+    "eslint-plugin-html": "^4.0.6",
+    "eslint-plugin-import": "^2.14.0",
     "fancy-log": "^1.3.2",
-    "friendly-errors-webpack-plugin": "^1.6.1",
     "gulp": "^4.0.0",
     "gulp-filter": "^5.0.1",
     "gulp-plumber": "^1.1.0",
     "gulp-uglify": "^3.0.0",
-    "html-webpack-plugin": "^3.2.0",
-    "husky": "^0.15.0-rc.13",
+    "html-webpack-plugin": "^4.0.0-beta.1",
+    "husky": "^1.1.2",
     "js-yaml": "^3.9.1",
+    "mini-css-extract-plugin": "^0.4.4",
+    "optimize-css-assets-webpack-plugin": "^5.0.1",
     "plugin-error": "^1.0.0",
-    "postcss-loader": "^2.0.6",
+    "postcss-loader": "^3.0.0",
     "postcss-scss": "2.0.0",
     "precss": "^3.1.1",
-    "svg-sprite-loader": "^3.5.1",
-    "svgo": "^1.0.3",
+    "svg-sprite-loader": "^4.1.2",
+    "svgo": "^1.1.1",
     "tape": "^4.9.0",
     "through2": "^2.0.3",
+    "url-loader": "^1.1.2",
     "vinyl": "^2.1.0",
-    "vue-loader": "^15.2.6",
-    "vue-style-loader": "^4.0.2",
-    "vue-template-compiler": "^2.4.2",
-    "webpack": "^3.5.6",
+    "vue-loader": "^15.4.2",
+    "vue-style-loader": "^4.1.2",
+    "vue-template-compiler": "^2.5.17",
+    "webpack": "^4.20.2",
+    "webpack-bundle-analyzer": "^3.0.2",
     "webpack-bundle-size-analyzer": "^2.7.0",
-    "wrapper-webpack-plugin": "1"
+    "wrapper-webpack-plugin": "2.0.0"
   },
   "author": "Gerald <[email protected]>",
   "repository": {
@@ -71,10 +82,11 @@
   "homepage": "https://github.com/violentmonkey/violentmonkey",
   "license": "MIT",
   "dependencies": {
-    "codemirror": "^5.35.0",
+    "@babel/runtime": "^7.1.2",
+    "codemirror": "^5.40.2",
     "core-js": "^2.5.1",
     "tldjs": "^2.3.1",
-    "vue": "^2.4.2",
+    "vue": "^2.5.17",
     "vueleton": "^0.6.1"
   },
   "engines": {

+ 11 - 0
scripts/eslint/vue.js

@@ -0,0 +1,11 @@
+module.exports = {
+  plugins: [
+    'html',
+  ],
+  rules: {
+    'import/extensions': ['error', 'always', {
+      js: 'never',
+      vue: 'never',
+    }],
+  },
+};

+ 21 - 0
scripts/template.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title><%= htmlWebpackPlugin.options.title %></title>
+<%
+if (htmlWebpackPlugin.options.manifest) {
+%><link rel="manifest" href="<%= htmlWebpackPlugin.options.manifest %>"><%
+}
+htmlWebpackPlugin.options.css.forEach(url => {
+%><link rel="stylesheet" href="<%= url %>"><%
+});
+htmlWebpackPlugin.options.inlineSource && htmlWebpackPlugin.files.css.forEach(file => {
+%><style><%= compilation.assets[file.slice(htmlWebpackPlugin.files.publicPath.length)].source() %></style><%
+});
+htmlWebpackPlugin.options.js.forEach(url => {
+%><script src="<%= url %>"></script><%
+});
+%></head>
+<body></body>
+</html>

+ 51 - 0
scripts/util.js

@@ -0,0 +1,51 @@
+const path = require('path');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+process.env.NODE_ENV = process.env.NODE_ENV || 'development';
+const isDev = process.env.NODE_ENV === 'development';
+const isProd = process.env.NODE_ENV === 'production';
+const isTest = process.env.NODE_ENV === 'test';
+const DIST = 'dist';
+const INIT_FUNC_NAME = 'VMInitInjection';
+
+function styleLoader(options) {
+  const {
+    loaders = [],
+    extract = isProd,
+    fallback = 'style-loader',
+    modules = false,
+  } = options || {};
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      modules,
+      importLoaders: 1,
+      sourceMap: false,
+    },
+  };
+  return [
+    extract ? MiniCssExtractPlugin.loader : fallback,
+    cssLoader,
+    ...loaders,
+  ];
+}
+
+function styleRule(options, rule) {
+  return {
+    test: /\.css$/,
+    use: styleLoader(options),
+    ...rule,
+  };
+}
+
+function resolve(dir) {
+  return path.join(__dirname, '..', dir);
+}
+
+exports.DIST = DIST;
+exports.INIT_FUNC_NAME = INIT_FUNC_NAME;
+exports.isDev = isDev;
+exports.isProd = isProd;
+exports.isTest = isTest;
+exports.styleLoader = styleLoader;
+exports.styleRule = styleRule;
+exports.resolve = resolve;

+ 0 - 66
scripts/utils.js

@@ -1,66 +0,0 @@
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
-process.env.NODE_ENV = process.env.NODE_ENV || 'development';
-const isDev = process.env.NODE_ENV === 'development';
-const isProd = process.env.NODE_ENV === 'production';
-const isTest = process.env.NODE_ENV === 'test';
-const INIT_FUNC_NAME = 'VMInitInjection';
-
-function styleLoader({
-  loaders = [],
-  extract = isProd,
-  minimize = isProd,
-  fallback = 'style-loader',
-} = {}) {
-  const cssLoader = {
-    loader: 'css-loader',
-    options: {
-      minimize,
-      importLoaders: 1,
-      sourceMap: false,
-    },
-  };
-  return extract ? ExtractTextPlugin.extract({
-    fallback,
-    use: [cssLoader, ...loaders],
-  }) : [
-    fallback,
-    cssLoader,
-    ...loaders,
-  ];
-}
-
-function styleRule(options = {}) {
-  return {
-    test: /\.css$/,
-    use: styleLoader(options),
-  };
-}
-
-function merge(obj1, obj2) {
-  if (!obj2) return obj1;
-  if (Array.isArray(obj1)) return obj1.concat(obj2);
-  const obj = Object.assign({}, obj1);
-  Object.keys(obj2).forEach(key => {
-    if (typeof obj[key] === 'object') {
-      obj[key] = merge(obj[key], obj2[key]);
-    } else {
-      obj[key] = obj2[key];
-    }
-  });
-  return obj;
-}
-
-exports.isDev = isDev;
-exports.isProd = isProd;
-exports.isTest = isTest;
-exports.styleLoader = styleLoader;
-exports.styleRule = styleRule;
-exports.merge = merge;
-exports.INIT_FUNC_NAME = INIT_FUNC_NAME;
-exports.definitions = {
-  'process.env': {
-    NODE_ENV: JSON.stringify(process.env.NODE_ENV),
-    DEBUG: isDev ? 'true' : 'false', // whether to log message errors
-    INIT_FUNC_NAME: JSON.stringify(INIT_FUNC_NAME),
-  },
-};

+ 0 - 7
scripts/vue-loader.conf.js

@@ -1,7 +0,0 @@
-const { isProd } = require('./utils');
-
-module.exports = {
-  compilerOptions: {
-    preserveWhitespace: false,
-  },
-};

+ 27 - 54
scripts/webpack.base.conf.js

@@ -1,60 +1,33 @@
-const path = require('path');
 const webpack = require('webpack');
-const MinifyPlugin = require('babel-minify-webpack-plugin');
-const VueLoaderPlugin = require('vue-loader/lib/plugin');
-const vueLoaderConfig = require('./vue-loader.conf');
-const { isDev, isProd, styleRule, definitions } = require('./utils');
+const { resolve, INIT_FUNC_NAME } = require('./util');
 
-const DIST = 'dist';
-const definePlugin = new webpack.DefinePlugin(definitions);
-
-function resolve(dir) {
-  return path.join(__dirname, '..', dir);
-}
-
-module.exports = {
-  output: {
-    path: resolve(DIST),
-    publicPath: '/',
-    filename: '[name].js',
-  },
-  resolve: {
-    // Tell webpack to look for peer dependencies in `node_modules`
-    // when packages are linked from outside directories
-    modules: [resolve('node_modules')],
-    extensions: ['.js', '.vue'],
-    alias: {
-      '#': resolve('src'),
-    }
-  },
-  module: {
-    rules: [
-      {
-        test: /\.vue$/,
-        loader: 'vue-loader',
-        options: vueLoaderConfig
+const baseConfig = [
+  require('./webpack/common')({
+    style: {
+      fallback: 'vue-style-loader',
+    },
+  }),
+  require('./webpack/url')(),
+  require('./webpack/raw')(),
+  require('./webpack/svg')(),
+  process.env.RUN_ENV === 'analyze' && require('./webpack/analyze')(),
+  require('./webpack/vue')(),
+]
+.filter(Boolean)
+.reduce(
+  (config, apply) => (apply && apply(config) || config),
+  {
+    resolve: {
+      alias: {
+        '#': resolve('src'),
       },
-      {
-        test: /\.js$/,
-        loader: 'babel-loader',
-        include: [resolve('src'), resolve('test')]
-      },
-      {
-        test: /\.svg$/,
-        loader: 'svg-sprite-loader',
-        include: [resolve('src/resources/icons')],
-      },
-      styleRule({
-        fallback: 'vue-style-loader',
-        loaders: ['postcss-loader'],
+    },
+    plugins: [
+      new webpack.DefinePlugin({
+        'process.env.INIT_FUNC_NAME': JSON.stringify(INIT_FUNC_NAME),
       }),
     ],
   },
-  // cheap-module-eval-source-map is faster for development
-  devtool: isDev ? '#inline-source-map' : false,
-  plugins: [
-    definePlugin,
-    new VueLoaderPlugin(),
-    isProd && new MinifyPlugin(),
-  ].filter(Boolean),
-};
+);
+
+module.exports = baseConfig;

+ 103 - 54
scripts/webpack.conf.js

@@ -1,71 +1,120 @@
-const path = require('path');
-const webpack = require('webpack');
-const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const HtmlWebpackPlugin = require('html-webpack-plugin');
 const WrapperWebpackPlugin = require('wrapper-webpack-plugin');
 const base = require('./webpack.base.conf');
-const { isProd, merge, INIT_FUNC_NAME } = require('./utils');
-
-const entry = {
-  'background/app': './src/background/app.js',
-  'options/app': './src/options/app.js',
-  'confirm/app': './src/confirm/app.js',
-  'popup/app': './src/popup/app.js',
-  injected: './src/injected/index.js',
+const { isProd, INIT_FUNC_NAME } = require('./util');
+const MINIFY = isProd && {
+  collapseWhitespace: true,
+  removeAttributeQuotes: true,
+  removeComments: true,
+  removeOptionalTags: true,
+  removeRedundantAttributes: true,
+  removeScriptTypeAttributes: true,
+  removeStyleLinkTypeAttributes: true,
+};
+const defaultTemplateOptions = {
+  minify: MINIFY,
+  template: 'scripts/template.html',
+  meta: {
+    viewport: 'width=device-width,initial-scale=1.0,user-scalable=no',
+  },
+  css: [],
+  js: [],
 };
 
-const targets = [];
-module.exports = targets;
+const targets = module.exports = [];
 
-targets.push(merge(base, {
-  entry,
+const pages = {
+  'browser': {
+    entry: './src/common/browser',
+  },
+  'background/index': {
+    entry: './src/background',
+    html: {},
+  },
+  'options/index': {
+    entry: './src/options',
+    html: {
+      js: [
+        '/public/lib/zip.js/zip.js',
+      ],
+    },
+  },
+  'confirm/index': {
+    entry: './src/confirm',
+    html: {},
+  },
+  'popup/index': {
+    entry: './src/popup',
+    html: {},
+  },
+  injected: {
+    entry: './src/injected',
+  },
+};
+const entries = Object.entries(pages)
+.reduce((res, [key, { entry }]) => Object.assign(res, { [key]: entry }), {});
+const htmlPlugins = Object.entries(pages)
+.map(([key, { html }]) => {
+  let options;
+  if (html) {
+    options = {
+      filename: `${key}.html`,
+      chunks: ['browser', key],
+      ...defaultTemplateOptions,
+    };
+    if (typeof html === 'function') {
+      options = html(options);
+    } else {
+      options = {
+        ...options,
+        ...html,
+      };
+    }
+  }
+  if (options) {
+    if (options.inlineSource) options.inject = false;
+    return new HtmlWebpackPlugin(options);
+  }
+})
+.filter(Boolean);
+
+targets.push({
+  ...base,
+  entry: entries,
+  optimization: {
+    ...base.optimization,
+    splitChunks: {
+      cacheGroups: {
+        common: {
+          name: 'common',
+          minChunks: 2,
+          chunks(chunk) {
+            return ![
+              'browser',
+              'injected',
+            ].includes(chunk.name);
+          },
+        },
+      },
+    },
+  },
   plugins: [
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'common',
-      chunks: Object.keys(entry).filter(name => name !== 'injected'),
-      minChunks: (m, c) => c >= 2,
-    }),
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'browser',
-      chunks: ['common', 'injected'],
-      minChunks: (m, c) => c >= 2,
-    }),
-    new HtmlWebpackPlugin({
-      filename: 'background/index.html',
-      chunks: ['browser', 'common', 'background/app'],
-    }),
-    new HtmlWebpackPlugin({
-      filename: 'options/index.html',
-      template: 'src/options/index.html',
-      chunks: ['browser', 'common', 'options/app'],
-    }),
-    new HtmlWebpackPlugin({
-      filename: 'confirm/index.html',
-      template: 'src/confirm/index.html',
-      chunks: ['browser', 'common', 'confirm/app'],
-    }),
-    new HtmlWebpackPlugin({
-      filename: 'popup/index.html',
-      template: 'src/popup/index.html',
-      chunks: ['browser', 'common', 'popup/app'],
-    }),
-    // new FriendlyErrorsPlugin(),
-    isProd && new ExtractTextPlugin('[name].css'),
-    // new webpack.NormalModuleReplacementPlugin(/\.\/rules\.json$/, resource => {
-    //   resource.request = path.resolve(__dirname, '../src/resources/empty-rules.json');
-    // }),
-  ].filter(Boolean),
-}));
+    ...base.plugins,
+    ...htmlPlugins,
+  ],
+});
 
-targets.push(merge(base, {
+targets.push({
+  ...base,
   entry: {
     'injected-web': './src/injected/web',
   },
   output: {
+    ...base.output,
     libraryTarget: 'commonjs2',
   },
   plugins: [
+    ...base.plugins,
     new WrapperWebpackPlugin({
       header: `\
 window.${INIT_FUNC_NAME} = function () {
@@ -77,4 +126,4 @@ window.${INIT_FUNC_NAME} = function () {
 };0;`,
     }),
   ],
-}));
+});

+ 3 - 4
scripts/webpack.test.conf.js

@@ -1,10 +1,9 @@
-const webpack = require('webpack');
 const base = require('./webpack.base.conf');
-const { merge } = require('./utils');
 
-module.exports = merge(base, {
+module.exports = {
+  ...base,
   target: 'node',
   entry: {
     test: './test',
   },
-});
+};

+ 8 - 0
scripts/webpack/analyze.js

@@ -0,0 +1,8 @@
+const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
+
+module.exports = () => config => {
+  config.plugins = [
+    ...config.plugins || [],
+    new BundleAnalyzerPlugin(),
+  ];
+};

+ 73 - 0
scripts/webpack/common.js

@@ -0,0 +1,73 @@
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+const { isProd, styleRule, resolve, DIST } = require('../util');
+
+module.exports = options => config => {
+  const { style } = options;
+  const defaultStyleOptions = {
+    loaders: ['postcss-loader'],
+  };
+  config.mode = isProd ? 'production' : 'development';
+  if (!isProd) config.devtool = 'inline-source-map';
+  config.output = {
+    path: resolve(DIST),
+    publicPath: '/',
+    filename: '[name].js',
+    ...config.output,
+  };
+  config.resolve = {
+    // Tell webpack to look for peer dependencies in `node_modules`
+    // when packages are linked from outside directories
+    modules: [resolve('node_modules')],
+    extensions: ['.js'],
+    ...config.resolve,
+  },
+  config.module = {
+    ...config.module,
+  };
+  config.module.rules = [
+    ...config.module.rules || [],
+    {
+      test: /\.js$/,
+      use: 'babel-loader',
+      include: [resolve('src'), resolve('test')],
+    },
+    // CSS modules: src/**/*.module.css
+    styleRule({
+      ...defaultStyleOptions,
+      ...style,
+      modules: true,
+    }, {
+      test: /\.module\.css$/,
+      exclude: [resolve('node_modules')],
+    }),
+    // normal CSS files: src/**/*.css
+    styleRule({ ...defaultStyleOptions, ...style }, {
+      exclude: [
+        /\.module\.css$/,
+        resolve('node_modules'),
+      ],
+    }),
+    // library CSS files: node_modules/**/*.css
+    styleRule(style, {
+      include: [resolve('node_modules')],
+    }),
+  ];
+  config.optimization = {
+    ...config.optimization,
+  };
+  config.optimization.minimizer = [
+    ...config.optimization.minimizer || [],
+    isProd && new UglifyJsPlugin({
+      cache: true,
+      parallel: true,
+      sourceMap: true // set to true if you want JS source maps
+    }),
+    isProd && new OptimizeCSSAssetsPlugin(),
+  ].filter(Boolean);
+  config.plugins = [
+    ...config.plugins || [],
+    isProd && new MiniCssExtractPlugin(),
+  ].filter(Boolean);
+};

+ 15 - 0
scripts/webpack/raw.js

@@ -0,0 +1,15 @@
+const { resolve } = require('../util');
+
+module.exports = () => config => {
+  config.module = {
+    ...config.module,
+  };
+  config.module.rules = [
+    ...config.module.rules || [],
+    {
+      test: /\.(html|vert|frag)$/,
+      use: 'raw-loader',
+      include: [resolve('src')],
+    },
+  ];
+};

+ 26 - 0
scripts/webpack/svg.js

@@ -0,0 +1,26 @@
+// const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
+const { resolve, isProd } = require('../util');
+// const extractSVG = isProd;
+
+module.exports = () => config => {
+  config.module = {
+    ...config.module,
+  };
+  config.module.rules = [
+    ...config.module.rules || [],
+    {
+      test: /\.svg$/,
+      use: [{
+        loader: 'svg-sprite-loader',
+        options: {
+          // extract: extractSVG,
+        },
+      }],
+      include: [resolve('src/resources/svg')],
+    },
+  ];
+  config.plugins = [
+    ...config.plugins || [],
+    // extractSVG && new SpriteLoaderPlugin(),
+  ].filter(Boolean);
+};

+ 12 - 0
scripts/webpack/sw.js

@@ -0,0 +1,12 @@
+const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
+const { isProd } = require('../util');
+
+module.exports = options => config => {
+  config.plugins = [
+    ...config.plugins || [],
+    new SWPrecacheWebpackPlugin({
+      minify: isProd,
+      ...options,
+    }),
+  ].filter(Boolean);
+};

+ 20 - 0
scripts/webpack/url.js

@@ -0,0 +1,20 @@
+const { resolve } = require('../util');
+
+module.exports = () => config => {
+  config.module = {
+    ...config.module,
+  };
+  config.module.rules = [
+    ...config.module.rules || [],
+    {
+      test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
+      use: [{
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+        },
+      }],
+      exclude: [resolve('src/resources/svg')],
+    },
+  ];
+};

+ 13 - 0
scripts/webpack/vue.js

@@ -0,0 +1,13 @@
+const VueLoaderPlugin = require('vue-loader/lib/plugin');
+
+module.exports = () => config => {
+  config.resolve.extensions.push('.vue');
+  config.module.rules.unshift({
+    test: /\.vue$/,
+    loader: 'vue-loader',
+    options: {
+      preserveWhitespace: false,
+    },
+  });
+  config.plugins.push(new VueLoaderPlugin());
+};

+ 0 - 18
src/.babelrc

@@ -1,18 +0,0 @@
-{
-  "presets": [
-    ["env", {
-      "modules": false,
-      "targets": {
-        "browsers": ["chrome >= 45"]
-      }
-    }],
-  ],
-  "plugins": [
-    ["transform-runtime", {
-      "polyfill": false,
-      // "helpers": false,
-    }],
-    "transform-export-extensions",
-  ],
-  "comments": false
-}

+ 0 - 1
src/background/app.js → src/background/index.js

@@ -1,4 +1,3 @@
-import '#/common/browser';
 import { noop } from '#/common';
 import { objectGet } from '#/common/object';
 import * as sync from './sync';

+ 4 - 4
src/background/utils/index.js

@@ -1,7 +1,7 @@
-export cache from './cache';
-export setClipboard from './clipboard';
-export checkUpdate from './update';
-export getEventEmitter from './events';
+export { default as cache } from './cache';
+export { default as setClipboard } from './clipboard';
+export { default as checkUpdate } from './update';
+export { default as getEventEmitter } from './events';
 export * from './script';
 export * from './options';
 export * from './requests';

+ 1 - 1
src/common/browser.js

@@ -123,5 +123,5 @@ const meta = {
 };
 if (typeof browser === 'undefined' && typeof chrome !== 'undefined') {
   global.browser = wrapAPIs(chrome, meta);
-  global.browser.__patched = true;
+  // global.browser.__patched = true;
 }

+ 1 - 1
src/common/ui/icon.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-const requireIcon = require.context('#/resources/icons', false, /\.svg$/);
+const requireIcon = require.context('#/resources/svg', false, /\.svg$/);
 requireIcon.keys().map(key => requireIcon(key));
 
 export default {

+ 0 - 13
src/confirm/index.html

@@ -1,13 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
-<title></title>
-<link rel="icon" type="image/png" href="/public/images/icon32.png" sizes="32x32">
-<link rel="icon" type="image/png" href="/public/images/icon16.png" sizes="16x16">
-</head>
-<body>
-<div id="app"></div>
-</body>
-</html>

+ 4 - 2
src/confirm/app.js → src/confirm/index.js

@@ -1,4 +1,3 @@
-import '#/common/browser';
 import Vue from 'vue';
 import { i18n } from '#/common';
 import '#/common/handlers';
@@ -11,7 +10,10 @@ Vue.prototype.i18n = i18n;
 document.title = `${i18n('labelInstall')} - ${i18n('extName')}`;
 
 options.ready(() => {
+  const el = document.createElement('div');
+  document.body.appendChild(el);
   new Vue({
     render: h => h(App),
-  }).$mount('#app');
+  })
+  .$mount(el);
 });

+ 0 - 1
src/injected/index.js

@@ -1,4 +1,3 @@
-import '#/common/browser';
 import { inject, getUniqId, sendMessage } from './utils';
 import initialize from './content';
 

+ 0 - 14
src/options/index.html

@@ -1,14 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
-<title></title>
-<link rel="icon" type="image/png" href="/public/images/icon32.png" sizes="32x32">
-<link rel="icon" type="image/png" href="/public/images/icon16.png" sizes="16x16">
-<script src="/public/lib/zip.js/zip.js"></script>
-</head>
-<body>
-<div id="app"></div>
-</body>
-</html>

+ 5 - 3
src/options/app.js → src/options/index.js

@@ -1,4 +1,3 @@
-import '#/common/browser';
 import Vue from 'vue';
 import {
   sendMessage, i18n, getLocaleString, cache2blobUrl,
@@ -25,9 +24,12 @@ function initialize() {
   document.title = i18n('extName');
   initMain();
   options.ready(() => {
+    const el = document.createElement('div');
+    document.body.appendChild(el);
     new Vue({
       render: h => h(App),
-    }).$mount('#app');
+    })
+    .$mount(el);
   });
 }
 
@@ -44,7 +46,7 @@ function initScript(script) {
   ].filter(Boolean).join('\n').toLowerCase();
   const name = script.custom.name || localeName;
   const lowerName = name.toLowerCase();
-  script._cache = { search, name, lowerName };
+  script.$cache = { search, name, lowerName };
 }
 
 function loadData(clear) {

+ 1 - 1
src/options/views/script-item.vue

@@ -2,7 +2,7 @@
   <div class="script" :class="{ disabled: !script.config.enabled, removed: script.config.removed }" :draggable="draggable" @dragstart.prevent="onDragStart">
     <img class="script-icon hidden-xs" :src="safeIcon">
     <div class="script-info flex">
-      <div class="script-name ellipsis flex-auto" v-text="script._cache.name"></div>
+      <div class="script-name ellipsis flex-auto" v-text="script.$cache.name"></div>
       <tooltip :title="i18n('labelAuthor') + script.meta.author" class="script-author ml-1 hidden-sm" v-if="author" align="end">
         <icon name="author"></icon>
         <a class="ellipsis ml-1" :href="`mailto:${author.email}`" v-if="author.email" v-text="author.name"></a>

+ 2 - 2
src/options/views/tab-installed.vue

@@ -155,7 +155,7 @@ export default {
       const lowerSearch = (search || '').toLowerCase();
       const { scripts } = this.store;
       const filteredScripts = search
-        ? scripts.filter(script => script._cache.search.includes(lowerSearch))
+        ? scripts.filter(script => script.$cache.search.includes(lowerSearch))
         : scripts.slice();
       if (sort.value === 'alpha') {
         const showEnabledFirst = options.get('filters.showEnabledFirst');
@@ -164,7 +164,7 @@ export default {
           if (showEnabledFirst) {
             keys.push(item.config.enabled ? 0 : 1);
           }
-          keys.push(item._cache.lowerName);
+          keys.push(item.$cache.lowerName);
           return keys.join('');
         };
         filteredScripts.sort((a, b) => {

+ 0 - 13
src/popup/index.html

@@ -1,13 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
-<title></title>
-<link rel="icon" type="image/png" href="/public/images/icon32.png" sizes="32x32">
-<link rel="icon" type="image/png" href="/public/images/icon16.png" sizes="16x16">
-</head>
-<body>
-<div id="app"></div>
-</body>
-</html>

+ 4 - 2
src/popup/app.js → src/popup/index.js

@@ -1,4 +1,3 @@
-import '#/common/browser';
 import Vue from 'vue';
 import { i18n, sendMessage } from '#/common';
 import handlers from '#/common/handlers';
@@ -11,9 +10,12 @@ tld.initTLD();
 
 Vue.prototype.i18n = i18n;
 
+const el = document.createElement('div');
+document.body.appendChild(el);
 new Vue({
   render: h => h(App),
-}).$mount('#app');
+})
+.$mount(el);
 
 Object.assign(handlers, {
   SetPopup(data, src) {

+ 0 - 0
src/resources/icons/arrow.svg → src/resources/svg/arrow.svg


+ 0 - 0
src/resources/icons/author.svg → src/resources/svg/author.svg


+ 0 - 0
src/resources/icons/code.svg → src/resources/svg/code.svg


+ 0 - 0
src/resources/icons/cog.svg → src/resources/svg/cog.svg


+ 0 - 0
src/resources/icons/filter.svg → src/resources/svg/filter.svg


+ 0 - 0
src/resources/icons/home.svg → src/resources/svg/home.svg


+ 0 - 0
src/resources/icons/info.svg → src/resources/svg/info.svg


+ 0 - 0
src/resources/icons/plus.svg → src/resources/svg/plus.svg


+ 0 - 0
src/resources/icons/question.svg → src/resources/svg/question.svg


+ 0 - 0
src/resources/icons/refresh.svg → src/resources/svg/refresh.svg


+ 0 - 0
src/resources/icons/search.svg → src/resources/svg/search.svg


+ 0 - 0
src/resources/icons/toggle-off.svg → src/resources/svg/toggle-off.svg


+ 0 - 0
src/resources/icons/toggle-on.svg → src/resources/svg/toggle-on.svg


+ 0 - 0
src/resources/icons/trash.svg → src/resources/svg/trash.svg


+ 0 - 0
src/resources/icons/undo.svg → src/resources/svg/undo.svg


File diff suppressed because it is too large
+ 896 - 87
yarn.lock


Some files were not shown because too many files changed in this diff