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.

344 line
8.7 KiB

5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
5 年之前
  1. 'use strict';
  2. /* eslint-disable @typescript-eslint/no-var-requires */
  3. const fs = require('fs');
  4. const path = require('path');
  5. const glob = require('glob');
  6. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  7. const { CleanWebpackPlugin: CleanPlugin } = require('clean-webpack-plugin');
  8. const CircularDependencyPlugin = require('circular-dependency-plugin');
  9. const CspHtmlPlugin = require('csp-html-webpack-plugin');
  10. // const ESLintPlugin = require('eslint-webpack-plugin');
  11. const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin');
  12. const HtmlExcludeAssetsPlugin = require('html-webpack-exclude-assets-plugin');
  13. const HtmlInlineSourcePlugin = require('html-webpack-inline-source-plugin');
  14. const HtmlPlugin = require('html-webpack-plugin');
  15. const ImageminPlugin = require('imagemin-webpack-plugin').default;
  16. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  17. const TerserPlugin = require('terser-webpack-plugin');
  18. module.exports = function(env, argv) {
  19. env = env || {};
  20. env.analyzeBundle = Boolean(env.analyzeBundle);
  21. env.analyzeDeps = Boolean(env.analyzeDeps);
  22. env.production = env.analyzeBundle || Boolean(env.production);
  23. env.optimizeImages = Boolean(env.optimizeImages) || (env.production && !env.analyzeBundle);
  24. if (!env.optimizeImages && !fs.existsSync(path.resolve(__dirname, 'images/settings'))) {
  25. env.optimizeImages = true;
  26. }
  27. // TODO: Total and complete HACK until the following issue is resolved
  28. // https://github.com/gr2m/universal-user-agent/issues/23
  29. const packageJSON = path.resolve(__dirname, 'node_modules/universal-user-agent/package.json');
  30. if (fs.existsSync(packageJSON)) {
  31. // eslint-disable-next-line import/no-dynamic-require
  32. const uua = require(packageJSON);
  33. if (uua.module !== 'dist-node/index.js') {
  34. console.log("Rewrote universal-user-agent's package.json module field to `dist-node/index.js`");
  35. uua.module = 'dist-node/index.js';
  36. fs.writeFileSync(packageJSON, `${JSON.stringify(uua, undefined, 4)}\n`, 'utf8');
  37. }
  38. }
  39. return [getExtensionConfig(env), getWebviewsConfig(env)];
  40. };
  41. function getExtensionConfig(env) {
  42. /**
  43. * @type any[]
  44. */
  45. const plugins = [
  46. new CleanPlugin({ cleanOnceBeforeBuildPatterns: ['**/*', '!**/webviews/**'] }),
  47. // new ESLintPlugin({
  48. // context: path.resolve(__dirname, 'src'),
  49. // files: '**/*.ts',
  50. // lintDirtyModulesOnly: true
  51. // })
  52. new ForkTsCheckerPlugin({
  53. async: false,
  54. eslint: true,
  55. useTypescriptIncrementalApi: true
  56. })
  57. ];
  58. if (env.analyzeDeps) {
  59. plugins.push(
  60. new CircularDependencyPlugin({
  61. cwd: __dirname,
  62. exclude: /node_modules/,
  63. failOnError: false,
  64. onDetected: function({ module: webpackModuleRecord, paths, compilation }) {
  65. if (paths.some(p => p.includes('container.ts'))) return;
  66. compilation.warnings.push(new Error(paths.join(' -> ')));
  67. }
  68. })
  69. );
  70. }
  71. if (env.analyzeBundle) {
  72. plugins.push(new BundleAnalyzerPlugin());
  73. }
  74. return {
  75. name: 'extension',
  76. entry: './src/extension.ts',
  77. mode: env.production ? 'production' : 'development',
  78. target: 'node',
  79. node: {
  80. __dirname: false
  81. },
  82. devtool: 'source-map',
  83. output: {
  84. libraryTarget: 'commonjs2',
  85. filename: 'extension.js',
  86. chunkFilename: 'feature-[name].js'
  87. },
  88. optimization: {
  89. minimizer: [
  90. new TerserPlugin({
  91. cache: true,
  92. parallel: true,
  93. sourceMap: true,
  94. terserOptions: {
  95. ecma: 8,
  96. // Keep the class names otherwise @log won't provide a useful name
  97. // eslint-disable-next-line @typescript-eslint/camelcase
  98. keep_classnames: true,
  99. module: true
  100. }
  101. })
  102. ],
  103. splitChunks: {
  104. cacheGroups: {
  105. vendors: false
  106. },
  107. chunks: 'async'
  108. }
  109. },
  110. externals: {
  111. vscode: 'commonjs vscode'
  112. },
  113. module: {
  114. rules: [
  115. {
  116. exclude: /\.d\.ts$/,
  117. include: path.resolve(__dirname, 'src'),
  118. test: /\.tsx?$/,
  119. use: {
  120. loader: 'ts-loader',
  121. options: {
  122. experimentalWatchApi: true,
  123. transpileOnly: true
  124. }
  125. }
  126. }
  127. ]
  128. },
  129. resolve: {
  130. // alias: {
  131. // 'universal-user-agent': 'node_modules/universal-user-agent/dist-node/index.js'
  132. // }
  133. extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  134. symlinks: false
  135. },
  136. plugins: plugins,
  137. stats: {
  138. all: false,
  139. assets: true,
  140. builtAt: true,
  141. env: true,
  142. errors: true,
  143. timings: true,
  144. warnings: true
  145. }
  146. };
  147. }
  148. function getWebviewsConfig(env) {
  149. const clean = ['**/*'];
  150. if (env.optimizeImages) {
  151. console.log('Optimizing images (src/webviews/apps/images/settings/*.png)...');
  152. clean.push(path.resolve(__dirname, 'images/settings/*'));
  153. }
  154. const cspPolicy = {
  155. 'default-src': "'none'",
  156. 'img-src': ['vscode-resource:', 'https:', 'data:'],
  157. 'script-src': ['vscode-resource:', "'nonce-Z2l0bGVucy1ib290c3RyYXA='"],
  158. 'style-src': ['vscode-resource:']
  159. };
  160. if (!env.production) {
  161. cspPolicy['script-src'].push("'unsafe-eval'");
  162. }
  163. /**
  164. * @type any[]
  165. */
  166. const plugins = [
  167. new CleanPlugin({ cleanOnceBeforeBuildPatterns: clean }),
  168. // new ESLintPlugin({
  169. // context: path.resolve(__dirname, 'src/webviews/apps'),
  170. // files: '**/*.ts',
  171. // lintDirtyModulesOnly: true
  172. // }),
  173. new ForkTsCheckerPlugin({
  174. tsconfig: path.resolve(__dirname, 'tsconfig.webviews.json'),
  175. async: false,
  176. eslint: true,
  177. useTypescriptIncrementalApi: true
  178. }),
  179. new MiniCssExtractPlugin({
  180. filename: '[name].css'
  181. }),
  182. new HtmlPlugin({
  183. excludeAssets: [/.+-styles\.js/],
  184. excludeChunks: ['welcome'],
  185. template: 'settings/index.html',
  186. filename: path.resolve(__dirname, 'dist/webviews/settings.html'),
  187. inject: true,
  188. inlineSource: env.production ? '.css$' : undefined,
  189. cspPlugin: {
  190. enabled: true,
  191. policy: cspPolicy,
  192. nonceEnabled: {
  193. 'script-src': true,
  194. 'style-src': true
  195. }
  196. },
  197. minify: env.production
  198. ? {
  199. removeComments: true,
  200. collapseWhitespace: true,
  201. removeRedundantAttributes: false,
  202. useShortDoctype: true,
  203. removeEmptyAttributes: true,
  204. removeStyleLinkTypeAttributes: true,
  205. keepClosingSlash: true,
  206. minifyCSS: true
  207. }
  208. : false
  209. }),
  210. new HtmlPlugin({
  211. excludeAssets: [/.+-styles\.js/],
  212. excludeChunks: ['settings'],
  213. template: 'welcome/index.html',
  214. filename: path.resolve(__dirname, 'dist/webviews/welcome.html'),
  215. inject: true,
  216. inlineSource: env.production ? '.css$' : undefined,
  217. cspPlugin: {
  218. enabled: true,
  219. policy: cspPolicy,
  220. nonceEnabled: {
  221. 'script-src': true,
  222. 'style-src': true
  223. }
  224. },
  225. minify: env.production
  226. ? {
  227. removeComments: true,
  228. collapseWhitespace: true,
  229. removeRedundantAttributes: false,
  230. useShortDoctype: true,
  231. removeEmptyAttributes: true,
  232. removeStyleLinkTypeAttributes: true,
  233. keepClosingSlash: true,
  234. minifyCSS: true
  235. }
  236. : false
  237. }),
  238. new HtmlExcludeAssetsPlugin(),
  239. new CspHtmlPlugin(),
  240. new ImageminPlugin({
  241. disable: !env.optimizeImages,
  242. externalImages: {
  243. context: path.resolve(__dirname, 'src/webviews/apps/images'),
  244. sources: glob.sync('src/webviews/apps/images/settings/*.png'),
  245. destination: path.resolve(__dirname, 'images')
  246. },
  247. cacheFolder: path.resolve(__dirname, 'node_modules', '.cache', 'imagemin-webpack-plugin'),
  248. gifsicle: null,
  249. jpegtran: null,
  250. optipng: null,
  251. pngquant: {
  252. quality: '85-100',
  253. speed: env.production ? 1 : 10
  254. },
  255. svgo: null
  256. }),
  257. new HtmlInlineSourcePlugin()
  258. ];
  259. return {
  260. name: 'webviews',
  261. context: path.resolve(__dirname, 'src/webviews/apps'),
  262. entry: {
  263. 'main-styles': ['./scss/main.scss'],
  264. settings: ['./settings/index.ts'],
  265. welcome: ['./welcome/index.ts']
  266. },
  267. mode: env.production ? 'production' : 'development',
  268. devtool: env.production ? undefined : 'eval-source-map',
  269. output: {
  270. filename: '[name].js',
  271. path: path.resolve(__dirname, 'dist/webviews'),
  272. publicPath: '#{root}/dist/webviews/'
  273. },
  274. module: {
  275. rules: [
  276. {
  277. exclude: /\.d\.ts$/,
  278. include: path.resolve(__dirname, 'src'),
  279. test: /\.tsx?$/,
  280. use: {
  281. loader: 'ts-loader',
  282. options: {
  283. configFile: 'tsconfig.webviews.json',
  284. experimentalWatchApi: true,
  285. transpileOnly: true
  286. }
  287. }
  288. },
  289. {
  290. test: /\.scss$/,
  291. use: [
  292. {
  293. loader: MiniCssExtractPlugin.loader
  294. },
  295. {
  296. loader: 'css-loader',
  297. options: {
  298. sourceMap: true,
  299. url: false
  300. }
  301. },
  302. {
  303. loader: 'sass-loader',
  304. options: {
  305. sourceMap: true
  306. }
  307. }
  308. ],
  309. exclude: /node_modules/
  310. }
  311. ]
  312. },
  313. resolve: {
  314. extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  315. modules: [path.resolve(__dirname, 'src/webviews/apps'), 'node_modules'],
  316. symlinks: false
  317. },
  318. plugins: plugins,
  319. stats: {
  320. all: false,
  321. assets: true,
  322. builtAt: true,
  323. env: true,
  324. errors: true,
  325. timings: true,
  326. warnings: true
  327. }
  328. };
  329. }