UNPKG

server-renderer

Version:

library of server side render for React

273 lines (272 loc) 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const path_1 = tslib_1.__importDefault(require("path")); const fs_1 = tslib_1.__importDefault(require("fs")); const webpack_1 = tslib_1.__importDefault(require("webpack")); const mini_css_extract_plugin_1 = tslib_1.__importDefault(require("mini-css-extract-plugin")); const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin")); const terser_webpack_plugin_1 = tslib_1.__importDefault(require("terser-webpack-plugin")); const optimize_css_assets_webpack_plugin_1 = tslib_1.__importDefault(require("optimize-css-assets-webpack-plugin")); // @ts-ignore const html_webpack_inline_source_plugin_1 = tslib_1.__importDefault(require("html-webpack-inline-source-plugin")); // @ts-ignore const postcss_safe_parser_1 = tslib_1.__importDefault(require("postcss-safe-parser")); const webpack_node_externals_1 = tslib_1.__importDefault(require("webpack-node-externals")); const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge")); const ora_1 = tslib_1.__importDefault(require("ora")); const config_1 = require("./config"); function createWebpackConfig(isServer) { const config = config_1.getConfig(); const resolveAppPath = (p) => path_1.default.resolve(config.rootDir, p); const plugins = getPlugins(isServer, config); const optimization = getOptimization(isServer, config); const webpackConfig = { bail: true, mode: config.isDev ? 'development' : 'production', // only client and in development mode devtool: (config.isDev && !isServer) ? 'cheap-module-eval-source-map' : false, stats: 'errors-warnings', target: isServer ? 'node' : 'web', entry: { app: isServer ? config.serverEntry : resolveAppPath('src/index.tsx') }, output: { path: path_1.default.join(config.distDir, isServer ? 'server' : 'client'), publicPath: isServer ? '/' : config.publicPath, filename: (!isServer && !config.isDev) ? 'js/[name].[chunkhash:5].js' : '[name].js', chunkFilename: (!isServer && !config.isDev) ? 'js/[name].[chunkhash:5].chunk.js' : '[name].chunk.js', libraryTarget: isServer ? 'commonjs' : 'umd', }, resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'], alias: { 'server-renderer': isServer ? 'server-renderer/lib/server.js' : 'server-renderer/lib/client.js', src: resolveAppPath('src'), }, }, module: { rules: [ { test: /\.(t|j)sx?$/, include: resolveAppPath('src'), loader: require.resolve('babel-loader'), options: { plugins: [ require.resolve('@babel/plugin-proposal-class-properties'), [ require.resolve('@babel/plugin-transform-runtime'), { helpers: true, regenerator: true, useESModules: false } ] ], presets: [ require.resolve('@babel/preset-react'), require.resolve('@babel/preset-env'), [ require.resolve('@babel/preset-typescript'), { isTSX: true, allExtensions: true, allowNamespaces: false, } ], ], } }, { test: /\.s?css$/, oneOf: [ { test: (modulePath) => { const basename = path_1.default.basename(modulePath); // 大写开头的就当做 css module return /^[A-Z]/.test(basename); }, use: getStyleLoaders(isServer, true, config), }, { use: getStyleLoaders(isServer, false, config), } ], }, ] }, externals: isServer ? [webpack_node_externals_1.default()] : undefined, plugins, optimization, node: { __dirname: isServer, __filename: isServer, } }; return mergeConfig(webpackConfig, isServer, config); } exports.createWebpackConfig = createWebpackConfig; function mergeConfig(webpackConfig, isServer, config) { if (config.configureWebpack) { if (typeof config.configureWebpack === 'function') { return config.configureWebpack(webpackConfig, isServer, config); } return webpack_merge_1.default(webpackConfig, config.configureWebpack); } return webpackConfig; } function getProgressPlugin() { const spinner = ora_1.default(); return new webpack_1.default.ProgressPlugin((percentage, msg) => { if (!spinner.isSpinning) { spinner.start(); } spinner.text = `${(percentage * 100).toFixed(2)}% ${msg}`; if (percentage === 1) { spinner.stop(); } }); } function getPlugins(isServer, config) { const envVariables = getEnvVariables(config); const plugins = [ new webpack_1.default.DefinePlugin(envVariables) ]; if (isServer || !config.isDev) { plugins.push(getProgressPlugin()); } if (!isServer && !config.isDev) { plugins.push(new mini_css_extract_plugin_1.default({ filename: 'style/[name].[contenthash:5].css', chunkFilename: 'style/[name].[contenthash:5].css', }), new html_webpack_plugin_1.default({ template: config.htmlTemplatePath, filename: config.builtHTMLPath, // 将 runtime.[hash].js 内联引入 inlineSource: /runtime.(\w)*\.js$/, }), new html_webpack_inline_source_plugin_1.default()); } return plugins; } function getOptimization(isServer, config) { if (isServer || config.isDev) { return undefined; } const useSourceMap = !isServer && !config.isDev; return { minimizer: [ new terser_webpack_plugin_1.default({ extractComments: false, terserOptions: { sourceMap: useSourceMap, safari10: true, output: { comments: false, } } }), new optimize_css_assets_webpack_plugin_1.default({ cssProcessorOptions: { parser: postcss_safe_parser_1.default, map: { inline: false, annotation: true, }, }, }), ], splitChunks: { chunks: 'all', name: true, cacheGroups: { vonders: { test: /[\\/]node_modules[\\/]/, name: 'vonders', priority: 10, }, }, }, runtimeChunk: { name: 'runtime', }, }; } function getStyleLoaders(isServer, isModule, config) { const useSourceMap = !isServer && !config.isDev; const cssLoader = { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules: isModule && { localIdentName: '[name]-[local]', }, // .hello-world 转成 classes.helloWorld localsConvention: 'camelCaseOnly', sourceMap: useSourceMap, }, }; const styleLoader = isServer ? require.resolve('isomorphic-style-loader') : config.isDev ? require.resolve('style-loader') : mini_css_extract_plugin_1.default.loader; const loaders = [ styleLoader, cssLoader, ]; if (!isServer && !config.isDev) { loaders.push({ loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), ], sourceMap: useSourceMap, } }); } loaders.push({ loader: require.resolve('sass-loader'), options: { data: config.sassData, sourceMap: useSourceMap, } }); return loaders; } function getEnvVariables(config) { const filename = `.env.${process.env.NODE_ENV}.local`; const fullPath = path_1.default.join(config.rootDir, filename); if (fs_1.default.existsSync(fullPath)) { const fileContent = fs_1.default.readFileSync(fullPath, 'utf-8'); fileContent.split('\n').forEach(line => { if (!line.includes('=')) { return; } const [key, value] = line.split('='); process.env[key] = value; }); } const variables = Object.keys(process.env) .reduce((variables, envKey) => { if (envKey.includes('APP_')) { variables[envKey] = process.env[envKey]; } return variables; }, { NODE_ENV: process.env.NODE_ENV, PORT: config.port }); return { 'process.env': Object.keys(variables).reduce((env, key) => { env[key] = JSON.stringify(variables[key]); return env; }, {}) }; }