You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
8.9 KiB

  1. 'use strict';
  2. const fs = require('fs');
  3. const glob = require('glob');
  4. const path = require('path');
  5. const webpack = require('webpack');
  6. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  7. const CleanPlugin = require('clean-webpack-plugin');
  8. const CircularDependencyPlugin = require('circular-dependency-plugin');
  9. const HtmlInlineSourcePlugin = require('html-webpack-inline-source-plugin');
  10. const HtmlPlugin = require('html-webpack-plugin');
  11. const ImageminPlugin = require('imagemin-webpack-plugin').default;
  12. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  13. const TerserPlugin = require('terser-webpack-plugin');
  14. module.exports = function(env, argv) {
  15. env = env || {};
  16. env.analyzeBundle = Boolean(env.analyzeBundle);
  17. env.analyzeDeps = Boolean(env.analyzeDeps);
  18. env.production = env.analyzeBundle || Boolean(env.production);
  19. env.optimizeImages = Boolean(env.optimizeImages) || (env.production && !env.analyzeBundle);
  20. if (!env.optimizeImages && !fs.existsSync(path.resolve(__dirname, 'images/settings'))) {
  21. env.optimizeImages = true;
  22. }
  23. return [getExtensionConfig(env), getUIConfig(env)];
  24. };
  25. function getExtensionConfig(env) {
  26. const plugins = [new CleanPlugin(), new webpack.IgnorePlugin(/^spawn-sync$/)];
  27. if (env.analyzeDeps) {
  28. plugins.push(
  29. new CircularDependencyPlugin({
  30. cwd: __dirname,
  31. exclude: /node_modules/,
  32. failOnError: false,
  33. onDetected({ module: webpackModuleRecord, paths, compilation }) {
  34. if (paths.some(p => /container\.ts/.test(p))) return;
  35. compilation.warnings.push(new Error(paths.join(' -> ')));
  36. }
  37. })
  38. );
  39. }
  40. if (env.analyzeBundle) {
  41. plugins.push(new BundleAnalyzerPlugin());
  42. }
  43. return {
  44. name: 'extension',
  45. entry: './src/extension.ts',
  46. mode: env.production ? 'production' : 'development',
  47. target: 'node',
  48. node: {
  49. __dirname: false
  50. },
  51. devtool: 'source-map',
  52. output: {
  53. libraryTarget: 'commonjs2',
  54. filename: 'extension.js'
  55. },
  56. optimization: {
  57. minimizer: [
  58. new TerserPlugin({
  59. cache: true,
  60. parallel: true,
  61. sourceMap: true,
  62. terserOptions: {
  63. ecma: 8,
  64. // Keep the class names otherwise @log won't provide a useful name
  65. keep_classnames: true,
  66. module: true
  67. }
  68. })
  69. ]
  70. },
  71. externals: {
  72. vscode: 'commonjs vscode'
  73. },
  74. module: {
  75. rules: [
  76. {
  77. test: /\.ts$/,
  78. enforce: 'pre',
  79. use: 'tslint-loader',
  80. exclude: /node_modules/
  81. },
  82. {
  83. test: /\.tsx?$/,
  84. use: 'ts-loader',
  85. exclude: /node_modules|\.d\.ts$/
  86. }
  87. ],
  88. // Removes `Critical dependency: the request of a dependency is an expression` from `./node_modules/vsls/vscode.js`
  89. exprContextRegExp: /^$/,
  90. exprContextCritical: false
  91. },
  92. resolve: {
  93. extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
  94. },
  95. plugins: plugins,
  96. stats: {
  97. all: false,
  98. assets: true,
  99. builtAt: true,
  100. env: true,
  101. errors: true,
  102. timings: true,
  103. warnings: true
  104. }
  105. };
  106. }
  107. function getUIConfig(env) {
  108. const clean = ['settings.html', 'welcome.html'];
  109. if (env.optimizeImages) {
  110. console.log('Optimizing images (src/ui/images/settings/*.png)...');
  111. clean.push('images/settings');
  112. }
  113. const plugins = [
  114. new CleanPlugin({ cleanOnceBeforeBuildPatterns: clean }),
  115. new MiniCssExtractPlugin({
  116. filename: '[name].css'
  117. }),
  118. new HtmlPlugin({
  119. excludeChunks: ['welcome'],
  120. template: 'settings/index.html',
  121. filename: path.resolve(__dirname, 'settings.html'),
  122. inject: true,
  123. inlineSource: env.production ? '.(js|css)$' : undefined,
  124. minify: env.production
  125. ? {
  126. removeComments: true,
  127. collapseWhitespace: true,
  128. removeRedundantAttributes: true,
  129. useShortDoctype: true,
  130. removeEmptyAttributes: true,
  131. removeStyleLinkTypeAttributes: true,
  132. keepClosingSlash: true,
  133. minifyCSS: true
  134. }
  135. : false
  136. }),
  137. new HtmlPlugin({
  138. excludeChunks: ['settings'],
  139. template: 'welcome/index.html',
  140. filename: path.resolve(__dirname, 'welcome.html'),
  141. inject: true,
  142. inlineSource: env.production ? '.(js|css)$' : undefined,
  143. minify: env.production
  144. ? {
  145. removeComments: true,
  146. collapseWhitespace: true,
  147. removeRedundantAttributes: true,
  148. useShortDoctype: true,
  149. removeEmptyAttributes: true,
  150. removeStyleLinkTypeAttributes: true,
  151. keepClosingSlash: true,
  152. minifyCSS: true
  153. }
  154. : false
  155. }),
  156. new HtmlInlineSourcePlugin(),
  157. new ImageminPlugin({
  158. disable: !env.optimizeImages,
  159. externalImages: {
  160. context: path.resolve(__dirname, 'src/ui/images'),
  161. sources: glob.sync('src/ui/images/settings/*.png'),
  162. destination: path.resolve(__dirname, 'images')
  163. },
  164. cacheFolder: path.resolve(__dirname, '.cache-images'),
  165. gifsicle: null,
  166. jpegtran: null,
  167. optipng: null,
  168. pngquant: {
  169. quality: '85-100',
  170. speed: env.production ? 1 : 10
  171. },
  172. svgo: null
  173. })
  174. ];
  175. return {
  176. name: 'ui',
  177. context: path.resolve(__dirname, 'src/ui'),
  178. // This is ugly having main.scss on both bundles, but if it is added separately it will generate a js bundle :(
  179. entry: {
  180. settings: ['./settings/index.ts', './scss/main.scss'],
  181. welcome: ['./welcome/index.ts', './scss/main.scss']
  182. // main: ['./scss/main.scss']
  183. },
  184. mode: env.production ? 'production' : 'development',
  185. devtool: env.production ? undefined : 'eval-source-map',
  186. output: {
  187. filename: '[name].js',
  188. path: path.resolve(__dirname, 'dist/ui'),
  189. publicPath: '{{root}}/dist/ui/'
  190. },
  191. module: {
  192. rules: [
  193. {
  194. test: /\.ts$/,
  195. enforce: 'pre',
  196. use: [
  197. {
  198. loader: 'tslint-loader',
  199. options: {
  200. tsConfigFile: 'ui.tsconfig.json'
  201. }
  202. }
  203. ],
  204. exclude: /node_modules/
  205. },
  206. {
  207. test: /\.tsx?$/,
  208. use: {
  209. loader: 'ts-loader',
  210. options: {
  211. configFile: 'ui.tsconfig.json'
  212. }
  213. },
  214. exclude: /node_modules|\.d\.ts$/
  215. },
  216. {
  217. test: /\.scss$/,
  218. use: [
  219. {
  220. loader: MiniCssExtractPlugin.loader
  221. },
  222. {
  223. loader: 'css-loader',
  224. options: {
  225. sourceMap: true,
  226. url: false
  227. }
  228. },
  229. {
  230. loader: 'sass-loader',
  231. options: {
  232. sourceMap: true
  233. }
  234. }
  235. ],
  236. exclude: /node_modules/
  237. }
  238. ]
  239. },
  240. resolve: {
  241. extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  242. modules: [path.resolve(__dirname, 'src/ui'), 'node_modules']
  243. },
  244. plugins: plugins,
  245. stats: {
  246. all: false,
  247. assets: true,
  248. builtAt: true,
  249. env: true,
  250. errors: true,
  251. timings: true,
  252. warnings: true
  253. }
  254. };
  255. }