webpack.plugin.config.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. const fs = require('fs');
  2. const path = require('path');
  3. const CopyWebpackPlugin = require('copy-webpack-plugin');
  4. const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin');
  5. const TerserPlugin = require('terser-webpack-plugin');
  6. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  7. const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
  8. const HtmlWebpackPlugin = require('html-webpack-plugin');
  9. import * as webpack from 'webpack';
  10. import { getStyleLoaders, getStylesheetEntries, getFileLoaders } from './webpack/loaders';
  11. interface WebpackConfigurationOptions {
  12. watch?: boolean;
  13. production?: boolean;
  14. }
  15. type WebpackConfigurationGetter = (options: WebpackConfigurationOptions) => webpack.Configuration;
  16. const findModuleTs = (base: string, files?: string[], result?: string[]) => {
  17. files = files || fs.readdirSync(base);
  18. result = result || [];
  19. if (files) {
  20. files.forEach(file => {
  21. const newbase = path.join(base, file);
  22. if (fs.statSync(newbase).isDirectory()) {
  23. result = findModuleTs(newbase, fs.readdirSync(newbase), result);
  24. } else {
  25. if (file.indexOf('module.ts') > -1) {
  26. // @ts-ignore
  27. result.push(newbase);
  28. }
  29. }
  30. });
  31. }
  32. return result;
  33. };
  34. const getModuleFiles = () => {
  35. return findModuleTs(path.resolve(process.cwd(), 'src'));
  36. };
  37. const getManualChunk = (id: string) => {
  38. if (id.endsWith('module.ts') || id.endsWith('module.tsx')) {
  39. const idx = id.lastIndexOf(path.sep + 'src' + path.sep);
  40. if (idx > 0) {
  41. const name = id.substring(idx + 5, id.lastIndexOf('.'));
  42. return {
  43. name,
  44. module: id,
  45. };
  46. }
  47. }
  48. return null;
  49. };
  50. const getEntries = () => {
  51. const entries: { [key: string]: string } = {};
  52. const modules = getModuleFiles();
  53. modules.forEach(modFile => {
  54. const mod = getManualChunk(modFile);
  55. // @ts-ignore
  56. entries[mod.name] = mod.module;
  57. });
  58. return {
  59. ...entries,
  60. ...getStylesheetEntries(),
  61. };
  62. };
  63. const getCommonPlugins = (options: WebpackConfigurationOptions) => {
  64. const packageJson = require(path.resolve(process.cwd(), 'package.json'));
  65. return [
  66. new MiniCssExtractPlugin({
  67. // both options are optional
  68. filename: 'styles/[name].css',
  69. }),
  70. new webpack.optimize.OccurrenceOrderPlugin(true),
  71. new CopyWebpackPlugin(
  72. [
  73. { from: 'plugin.json', to: '.' },
  74. { from: '../README.md', to: '.' },
  75. { from: '../LICENSE', to: '.' },
  76. { from: 'img/*', to: '.' },
  77. { from: '**/*.json', to: '.' },
  78. { from: '**/*.svg', to: '.' },
  79. { from: '**/*.png', to: '.' },
  80. { from: '**/*.html', to: '.' },
  81. ],
  82. { logLevel: options.watch ? 'silent' : 'warn' }
  83. ),
  84. new ReplaceInFileWebpackPlugin([
  85. {
  86. dir: 'dist',
  87. files: ['plugin.json', 'README.md'],
  88. rules: [
  89. {
  90. search: '%VERSION%',
  91. replace: packageJson.version,
  92. },
  93. {
  94. search: '%TODAY%',
  95. replace: new Date().toISOString().substring(0, 10),
  96. },
  97. ],
  98. },
  99. ]),
  100. ];
  101. };
  102. export const getWebpackConfig: WebpackConfigurationGetter = options => {
  103. const plugins = getCommonPlugins(options);
  104. const optimization: { [key: string]: any } = {};
  105. if (options.production) {
  106. optimization.minimizer = [new TerserPlugin(), new OptimizeCssAssetsPlugin()];
  107. } else if (options.watch) {
  108. plugins.push(new HtmlWebpackPlugin());
  109. }
  110. return {
  111. mode: options.production ? 'production' : 'development',
  112. target: 'web',
  113. node: {
  114. fs: 'empty',
  115. net: 'empty',
  116. tls: 'empty',
  117. },
  118. context: path.join(process.cwd(), 'src'),
  119. devtool: 'source-map',
  120. entry: getEntries(),
  121. output: {
  122. filename: '[name].js',
  123. path: path.join(process.cwd(), 'dist'),
  124. libraryTarget: 'amd',
  125. publicPath: '/',
  126. },
  127. performance: { hints: false },
  128. externals: [
  129. 'lodash',
  130. 'jquery',
  131. 'moment',
  132. 'slate',
  133. 'emotion',
  134. 'prismjs',
  135. 'slate-plain-serializer',
  136. 'slate-react',
  137. 'react',
  138. 'react-dom',
  139. 'rxjs',
  140. 'd3',
  141. 'angular',
  142. '@grafana/ui',
  143. '@grafana/runtime',
  144. '@grafana/data',
  145. // @ts-ignore
  146. (context, request, callback) => {
  147. const prefix = 'grafana/';
  148. if (request.indexOf(prefix) === 0) {
  149. return callback(null, request.substr(prefix.length));
  150. }
  151. // @ts-ignore
  152. callback();
  153. },
  154. ],
  155. plugins,
  156. resolve: {
  157. extensions: ['.ts', '.tsx', '.js'],
  158. modules: [path.resolve(process.cwd(), 'src'), 'node_modules'],
  159. },
  160. module: {
  161. rules: [
  162. {
  163. test: /\.tsx?$/,
  164. loaders: [
  165. {
  166. loader: 'babel-loader',
  167. options: {
  168. presets: ['@babel/preset-env'],
  169. plugins: ['angularjs-annotate'],
  170. },
  171. },
  172. {
  173. loader: 'ts-loader',
  174. options: { onlyCompileBundledFiles: true },
  175. },
  176. ],
  177. exclude: /(node_modules)/,
  178. },
  179. ...getStyleLoaders(),
  180. {
  181. test: /\.html$/,
  182. exclude: [/node_modules/],
  183. use: {
  184. loader: 'html-loader',
  185. },
  186. },
  187. ...getFileLoaders(),
  188. ],
  189. },
  190. optimization,
  191. // optimization: {
  192. // splitChunks: {
  193. // chunks: 'all',
  194. // name: 'shared'
  195. // }
  196. // }
  197. };
  198. };