generateConfig.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. const path = require('path');
  2. const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
  3. // Webpack plugins.
  4. const { DefinePlugin } = require('webpack');
  5. const { LimitChunkCountPlugin } = require('webpack').optimize;
  6. const HtmlWebpackPlugin = require('html-webpack-plugin');
  7. const VirtualModulesPlugin = require('webpack-virtual-modules');
  8. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  9. const ProgressBarPlugin = require('progress-bar-webpack-plugin');
  10. const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
  11. const CopyPlugin = require("copy-webpack-plugin");
  12. var dotenv = require('dotenv').config({ path: path.resolve(__dirname + '../../../../.env') });
  13. import MyHtmlBeautifyWebpackPlugin from '../webpack-plugins/MyHtmlBeautifyWebpackPlugin';
  14. import { ConfigType, ConfigPages } from '../config';
  15. import defaultPages from './pagesConfig';
  16. import formatPagesConfig from './formatPagesConfig';
  17. function webpackEntry(env: string, srcDir: string, pages: ConfigPages) {
  18. const ret: { [key: string]: string } = {};
  19. for (const p in pages) {
  20. if ('development' === env || !pages[p].buildExclude) {
  21. ret[p] = path.resolve(srcDir + '/' + p + '.js');
  22. }
  23. }
  24. return ret;
  25. }
  26. function webpackOutput(env: string, destinationDir: string, buildDir?: string, chunkhash?: string, hash?: string) {
  27. const ret = {
  28. path: destinationDir,
  29. filename: '',
  30. };
  31. const prefix = 'development' === env ? '' : buildDir;
  32. let tmp;
  33. if (undefined !== chunkhash) {
  34. tmp = chunkhash.trim();
  35. if ('' === tmp) {
  36. throw Error('Invalid chunkhash argument value: ' + chunkhash);
  37. }
  38. ret.filename = (prefix || '') + '[name]-[chunkhash].js';
  39. }
  40. else if (undefined !== hash) {
  41. tmp = hash.trim();
  42. if ('' === tmp) {
  43. throw Error('Invalid hash argument value: ' + hash);
  44. }
  45. ret.filename = (prefix || '') + '[name]-[hash].js';
  46. }
  47. else {
  48. ret.filename = (prefix || '') + '[name].js';
  49. }
  50. return ret;
  51. }
  52. function webpackAlias() {
  53. return {
  54. // modernizr$: path.resolve(__dirname, "../../.modernizrrc"), // TODO: Enable this?
  55. };
  56. }
  57. function webpackRules(env: string, srcDir: string, postcssConfigFile: string): any[] {
  58. return [{
  59. test: /\.(jsx|js)?$/,
  60. use: 'babel-loader'
  61. },
  62. {
  63. test: /\.(tsx|ts)?$/,
  64. use: 'ts-loader',
  65. // exclude: /node_modules/,
  66. // options: {
  67. // compilerOptions: {
  68. // "sourceMap": !isProduction,
  69. // },
  70. // },
  71. },
  72. {
  73. test: /\.ejs$/,
  74. use: {
  75. loader: 'ejs-compiled-loader',
  76. options: {
  77. // beautify: true,
  78. htmlmin: true,
  79. // htmlminOptions: {
  80. // removeComments: true,
  81. // collapseWhitespace: true,
  82. // preserveLineBreaks: true
  83. // }
  84. }
  85. }
  86. },
  87. {
  88. test: /\.(sa|sc|c)ss$/,
  89. use: [
  90. { loader: MiniCssExtractPlugin.loader },
  91. // { loader: 'development' === env ? MiniCssExtractPlugin.loader : 'style-loader' }, // Use inline <style> tag.
  92. { loader: 'css-loader', options: { importLoaders: 1 } },
  93. { loader: 'postcss-loader', options: { postcssOptions: { config: postcssConfigFile } } },
  94. { loader: 'sass-loader' },
  95. ],
  96. },
  97. {
  98. test: /\.module\.(sa|sc|c)ss$/,
  99. use: [
  100. { loader: MiniCssExtractPlugin.loader },
  101. // { loader: 'development' === env ? MiniCssExtractPlugin.loader : 'style-loader' }, // Use inline <style> tag.
  102. { loader: 'css-loader', options: { importLoaders: 1, modules: true, onlyLocals: false } },
  103. { loader: 'postcss-loader', options: { postcssOptions: { config: postcssConfigFile } } },
  104. { loader: 'sass-loader' },
  105. ]
  106. },
  107. {
  108. test: /\.(png|jpe?g|gif)(\?\S*)?$/,
  109. use: {
  110. loader: 'url-loader',
  111. options: {
  112. limit: 1024,
  113. fallback: 'file-loader',
  114. name: (file: string) => {
  115. return '.' + path.join(file.replace(srcDir, ''), '..').replace(/\\/g, '/') + '/[name].[ext]';
  116. },
  117. },
  118. },
  119. },
  120. {
  121. test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  122. /*issuer: {
  123. test: /\.jsx?$/
  124. },*/
  125. use: ['babel-loader', '@svgr/webpack', 'url-loader']
  126. },
  127. {
  128. test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  129. loader: 'url-loader'
  130. },
  131. {
  132. test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
  133. use: [{
  134. loader: 'file-loader',
  135. options: {
  136. name: (file: string) => {
  137. return '.' + path.join(file.replace(srcDir, ''), '..').replace(/\\/g, '/') + '/[name].[ext]';
  138. },
  139. }
  140. }]
  141. },
  142. {
  143. test: /\.modernizrrc.js$/,
  144. use: 'modernizr-loader',
  145. },
  146. {
  147. test: /\.modernizrrc(\.json)?$/,
  148. use: ['modernizr-loader', 'json-loader'],
  149. }
  150. ];
  151. }
  152. function webpackPlugins(env: string, srcDir: string, pages: ConfigPages, cssSrc: string) {
  153. const ret = [
  154. new DefinePlugin({ "process.env": JSON.stringify(dotenv.parsed) }),
  155. new NodePolyfillPlugin(),
  156. new MyHtmlBeautifyWebpackPlugin(),
  157. ];
  158. if ('development' !== env) {
  159. ret.push(
  160. new CopyPlugin({
  161. patterns: [
  162. {
  163. from: path.resolve(__dirname, '../../../src/static/lib'),
  164. to: path.resolve(__dirname, '../../../' + env + '/static/lib'),
  165. },
  166. {
  167. from: path.resolve(__dirname, '../../../src/static/images'),
  168. to: path.resolve(__dirname, '../../../' + env + '/static/images'),
  169. },
  170. {
  171. from: path.resolve(__dirname, '../../../src/static/favicons'),
  172. to: path.resolve(__dirname, '../../../' + env + '/static/favicons'),
  173. },
  174. {
  175. from: path.resolve(__dirname, '../../../src/static/css/_extra.css'),
  176. to: path.resolve(__dirname, '../../../' + env + '/static/css/_extra.css'),
  177. },
  178. ],
  179. })
  180. );
  181. }
  182. const virtualPages: { [key: string]: string } = {};
  183. let file: string;
  184. for (const k in pages) {
  185. if ('production' !== env || !pages[k].buildExclude) {
  186. file = path.resolve(srcDir + '/' + k + '.js');
  187. if ((void 0 !== pages[k].staticPage && pages[k].staticPage) || void 0 === pages[k].render) {
  188. virtualPages[file] = '';
  189. } else {
  190. virtualPages[file] = pages[k].render;
  191. }
  192. }
  193. if ('development' === env) {
  194. // Export pages HTML files.
  195. ret.push(new HtmlWebpackPlugin({
  196. template: path.resolve(__dirname, '../templates/index.ejs'),
  197. hash: false,
  198. chunks: [k],
  199. ...pages[k],
  200. }));
  201. }
  202. }
  203. ret.push(new VirtualModulesPlugin(virtualPages));
  204. ret.push(new MiniCssExtractPlugin({
  205. ignoreOrder: true, // TODO: Remove it...
  206. // filename: ! is_build ? '[name].css' : '[name].[hash].css',
  207. // chunkFilename: ! is_build ? '[id].css' : '[id].[hash].css',
  208. filename: cssSrc + '[name].css',
  209. // chunkFilename: "../css/[id].css",
  210. }));
  211. if ('development' !== env) {
  212. ret.push(new LimitChunkCountPlugin({ maxChunks: 1 }));
  213. ret.push(
  214. new ProgressBarPlugin({
  215. clear: false,
  216. })
  217. );
  218. }
  219. if ('production' === env) {
  220. ret.push(new CssMinimizerPlugin({
  221. cache: true, // TODO: Ignore in Webpack 5. Use https://webpack.js.org/configuration/other-options/#cache.
  222. minimizerOptions: {
  223. preset: [
  224. 'default',
  225. {
  226. discardComments: { removeAll: true },
  227. },
  228. ],
  229. },
  230. }));
  231. }
  232. return ret;
  233. }
  234. export default function generateConfig(env: string, config: ConfigType) {
  235. const srcDir = config.src;
  236. const buildDir = config.build + '/' + env + ('development' === env ? '' : '/static');
  237. const cssbuild = './css/';
  238. const jsbuild = './js/';
  239. const configPages = config.pages;
  240. const configPagesKeys = config.pages ? Object.keys(configPages) : [];
  241. const defPages = defaultPages(configPagesKeys);
  242. const pages = formatPagesConfig(
  243. { title: '', filename: '', render: '', html: config.html, window: config.window },
  244. { ...configPages, ...defPages }
  245. );
  246. const ret = {
  247. entry: webpackEntry(env, srcDir, pages),
  248. output: 'development' === env ? webpackOutput(env, srcDir, void 0, void 0, void 0) : webpackOutput(env, buildDir, jsbuild, void 0, void 0),
  249. plugins: webpackPlugins(env, srcDir, pages, cssbuild),
  250. module: {
  251. rules: webpackRules(env, srcDir, config.postcssConfigFile),
  252. },
  253. resolve: {
  254. alias: webpackAlias(),
  255. extensions: ['.tsx', '.ts', '.jsx', '.js'],
  256. },
  257. };
  258. return ret;
  259. }