Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import chalk from 'chalk' import * as os from 'os' import * as path from 'path' import * as querystring from 'querystring' import * as webpack from 'webpack' import * as wbmerge from 'webpack-merge' import { devLogger, devLoggerTitle, esWbformatter, getFirstValidObject, IFullTimplaConfig, isPackageInstalled, ITimplaCustomizeWebpackConfig, ITimplaWebpackEntry, IWebpackObjectEntry, lazyImport, logTitle, pathToUrl, projectDestPath, projectPath, projectSrcPath, sureLazyImport, TIMPLA_PROCESS as TP, webpackManifest, } from '../internal' const invalidJsEntryMessage = ` Invalid javascripts entry supplied. Timpla only accepts the object-style format: { [name: string]: string[] } ` const ensureLeadingDot = (x: string) => { return x.indexOf('.') === 0 ? x : `.${x}` } export const webpackMultiConfig = (timplaConfig: IFullTimplaConfig) => ( env: 'development' | 'production' ) => { const jsConfig = timplaConfig.javascripts const devConfig = timplaConfig.development const jsSrc = projectSrcPath(jsConfig.src) const jsDest = projectDestPath(jsConfig.dest) const publicPath = pathToUrl('/', jsConfig.publicPath || jsConfig.dest, '/') const useDevESLint = TP.isDevelopment && !!devConfig.eslint const babelLoaderOptions = jsConfig.babelLoaderOptions const esLintLoaderOptions = useDevESLint && devConfig.eslint.esLintLoaderOptions const useTypeScript = jsConfig.useTypeScript const useDevTSLint = TP.isDevelopment && !!devConfig.tslint && useTypeScript const forkTsCheckerOptions = useDevTSLint && devConfig.tslint && devConfig.tslint.forkTsCheckerOptions const mode = TP.BABEL_ENV || TP.NODE_ENV || env const useProdRev = TP.isProduction && timplaConfig.production.rev const webpackHotMiddlewareClientOptions = devConfig.webpackHotMiddlewareClient const hotEnabled = !!webpackHotMiddlewareClientOptions const useDevHmr = TP.isDevelopment && hotEnabled const webpackBundleAnalyzerOptions = jsConfig.webpackBundleAnalyzerOptions const extensions = jsConfig.extensions.map(ensureLeadingDot) const resolve = (...filePaths: string[]) => { const files = filePaths.filter(Boolean) if (!files.every(e => typeof e === 'string') || !files.length) { throw Error( chalk.red('Invalid arguments passed to the Timpla resolve function. Please pass strings.') ) } return filePaths.map(filePath => projectSrcPath(jsConfig.src, filePath)) } /** Prepares the Webpack entry config */ const getEntryObject = (ent: ITimplaWebpackEntry | IWebpackObjectEntry) => { let derivedEntry = ent if (typeof ent === 'function') { derivedEntry = ent({ resolve }) } if (typeof derivedEntry !== 'object') { throw Error(chalk.red(invalidJsEntryMessage)) } const normalisedArrays = Object.entries(derivedEntry).reduce( (acc: { [n: string]: any }, [entryKey, entryValue]) => { acc[entryKey] = Array.isArray(entryValue) ? entryValue : [entryValue] return acc }, {} ) return normalisedArrays } const webpackEntry = getEntryObject(jsConfig.entry) /** * Bundle analyzer can be disabled through a boolean. * If an object is provided, we use that and set defaults, so timpla can work accordingly. */ const bundleAnalyzer = () => { if (!webpackBundleAnalyzerOptions) { return false } const { BundleAnalyzerPlugin } = lazyImport('webpack-bundle-analyzer') const bundleAnalyzerConfig = getFirstValidObject(webpackBundleAnalyzerOptions, {}) const merged = { ...bundleAnalyzerConfig, ...(TP.isDevelopment ? { analyzerMode: 'server', // To not drive the user insane, we have to check if we've reloaded timpla openAnalyzer: !TP.isTimplaReloaded && bundleAnalyzerConfig.openAnalyzer, } : { // opening analyzerMode in prod causes the build process to hang // the user will need to manually execute npx timpla openAnalyzer to view the report analyzerMode: 'disabled', generateStatsFile: true, }), } return new BundleAnalyzerPlugin(merged) } const wbConfigDefault = () => { return { context: TP.INIT_CWD, entry: webpackEntry, // Tell Webpack to provide empty mocks for them so importing them works. // Some libraries import Node modules but don't use them in the browser. node: { dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty' }, optimization: { minimizer: [] }, output: { path: path.normalize(jsDest), filename: '[name].js', publicPath }, // Turn off performance processing because we utilize // our own hints via the FileSizeReporter performance: false, plugins: [ // Moment.js is an extremely popular library that bundles large locale files // by default due to how Webpack interprets its code. This is a practical // solution that requires the user to opt into importing specific locales. // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // You can remove this if you don't use Moment.js: new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // See https://github.com/facebook/create-react-app/issues/240 // a plugin that prints an error when you attempt to do this. // Watcher doesn't work well if you mistype casing in a path so we use bundleAnalyzer(), ].filter(Boolean), resolve: { extensions, alias: {}, modules: [jsSrc, projectPath('node_modules')] }, resolveLoader: { modules: [projectPath('node_modules')] }, } } const wbConfigProd = () => { const HardSourceWebpackPlugin = lazyImport('hard-source-webpack-plugin') const TerserPlugin = sureLazyImport('terser-webpack-plugin') const prodConfig = jsConfig.production const terserConfig = { ...jsConfig.production.terserConfig, sourceMap: !!prodConfig.devtool, } const hardSourceOptions = prodConfig.hardSourceOptions return { // Fail out on the first error instead of tolerating it. bail: true, devtool: prodConfig.devtool || false, module: { strictExportPresence: true }, optimization: { minimizer: [new TerserPlugin(terserConfig)], }, plugins: [hardSourceOptions && new HardSourceWebpackPlugin(hardSourceOptions)].filter( Boolean ), } } const wbConfigRev = () => ({ output: { filename: '[name]-[hash].js', }, plugins: [webpackManifest(jsConfig.dest, timplaConfig.dest)], }) /** * Outputs an entry config that will be merged with the jsconfig above... */ const wbConfigHmr = () => { const hotMiddleware = `webpack-hot-middleware/client?${querystring.stringify( webpackHotMiddlewareClientOptions )}` const injectHmr = (acc: { [n: string]: any }, key: string) => { // Since we're merging with another array acc[key] = [hotMiddleware] return acc } const injectedEntries = Object.keys(webpackEntry).reduce(injectHmr, {}) return { entry: injectedEntries, plugins: [new webpack.HotModuleReplacementPlugin()], resolve: { alias: { 'react-dom': '@hot-loader/react-dom', }, }, } } const wbConfigDev = () => ({ devtool: devConfig.devtool, output: { pathinfo: true, }, }) /** * There will be many ways to configure the babel-loader. * To make it fool-proof: * a) The entire webpack configuration will be available to configure in .timplaconfig.js * b) Pick up the user's babel config file from their project root */ const wbConfigBabelLoader = () => { return { module: { rules: [ { exclude: /node_modules/, test: new RegExp(`(\\${extensions.join('$|')}$)`), use: { loader: 'babel-loader', options: { // Don't waste time on Gzipping the cache cacheCompression: false, // Enable caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, // Set the root to where the project src root: TP.INIT_CWD, ...babelLoaderOptions, }, }, }, ], }, } } const wbConfigESLint = () => { isPackageInstalled('eslint-loader') return { module: { rules: [ { exclude: /node_modules/, test: new RegExp(`(\\${extensions.filter(ext => !ext.includes('ts')).join('$|')}$)`), use: { loader: 'eslint-loader', options: { formatter: esWbformatter({ basePath: timplaConfig.src, }), ...esLintLoaderOptions, }, }, }, ], }, } } /** * In order to enable TypeScript, add the @babel/preset-typescript * TypeScript and TSLint are set as peerDependencies, * so you can bring over your preferred versions */ const wbConfigTSLint = () => { const ForkTsCheckerWebpackPlugin = lazyImport('fork-ts-checker-webpack-plugin') const tsUtils = sureLazyImport('./tsUtils') return { plugins: [ new ForkTsCheckerWebpackPlugin({ async: false, formatter: tsUtils.tsWbFormatter, tslint: true, watch: jsSrc, ...forkTsCheckerOptions, }), ], } } const logHmrMessage = (isHot: boolean) => { const hmrColor = isHot ? 'green' : 'yellow' const hmrMessage = isHot ? 'running' : 'turned off' logTitle(chalk[hmrColor](`Webpack: Hot Module Reload ${hmrMessage}.`)) } // All configs are functions const configs = [ wbConfigDefault, wbConfigBabelLoader, useDevESLint && wbConfigESLint, useDevTSLint && wbConfigTSLint, useDevHmr && wbConfigHmr, TP.isProduction && wbConfigProd, useProdRev && wbConfigRev, TP.isDevelopment && wbConfigDev, ] .filter(Boolean) .map((cb: any) => cb()) const webpackConfig = wbmerge(...configs, { mode: mode as 'development' | 'production', }) // Allow customisation of webpack config via task config method const { customizeWebpackConfig = ({ webpackConfig: w }) => w, }: { customizeWebpackConfig: ITimplaCustomizeWebpackConfig } = jsConfig const fullWebpackConfig = customizeWebpackConfig({ projectDestPath, projectSrcPath, timplaConfig, timplaProcess: TP, webpack, webpackConfig, webpackMerge: wbmerge, }) devLoggerTitle('Timpla process') devLogger('%O', TP, os.EOL) devLoggerTitle('Webpack config') devLogger('%O', fullWebpackConfig, os.EOL) if (TP.isDevelopment) { logHmrMessage(useDevHmr) } // If MEASURE=any, wrap the whole config with SpeedMeasure // This allows us to visualiase how loaders and modules affect // Webpack builds // @TODO - buggy? if (TP.MEASURE) { const SpeedMeasurePlugin = lazyImport('speed-measure-webpack-plugin') const smp = new SpeedMeasurePlugin() return smp.wrap(fullWebpackConfig) } return fullWebpackConfig } |