diff --git a/lib/pipeline/webpackProcess.js b/lib/pipeline/webpackProcess.js index ef420626..0481e80d 100644 --- a/lib/pipeline/webpackProcess.js +++ b/lib/pipeline/webpackProcess.js @@ -1,12 +1,8 @@ -const async = require('async'); -const webpack = require('webpack'); -const utils = require('../utils/utils'); -const fs = require('../core/fs'); const constants = require('../constants'); -const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); +const fs = require('../core/fs'); const ProcessWrapper = require('../core/processes/processWrapper'); -const path = require('path'); -const glob = require('glob'); +const webpack = require('webpack'); +const writeFile = require('util').promisify(require('fs').writeFile); let webpackProcess; @@ -14,195 +10,120 @@ class WebpackProcess extends ProcessWrapper { constructor(options) { super(options); this.webpackConfigName = options.webpackConfigName; + if (!process.env.DAPP_PATH) { + process.env.DAPP_PATH = fs.dappPath(); + } + if (!process.env.EMBARK_PATH) { + process.env.EMBARK_PATH = fs.embarkPath(); + } } - build(file, importsList, callback) { + async build(assets, importsList, callback) { + try { + await this.webpackRun(assets, importsList, callback); + } catch (e) { + console.error(e.message); + callback(e); + // ?? should return e or e.message + } + } + + async webpackRun(assets, importsList, callback) { const self = this; - let realCwd; + try { + await writeFile( + fs.dappPath('.embark/embark-aliases.json'), + JSON.stringify(importsList) + ); + await writeFile( + fs.dappPath('.embark/embark-assets.json'), + JSON.stringify(assets) + ); + } catch (e) { + console.error(e.message); + return callback(e); + // ?? should return e or e.message + } - async.waterfall([ - function changeCwd(next) { - realCwd = utils.pwd(); - process.chdir(fs.embarkPath('')); - next(); - }, + const dappConfigPath = fs.dappPath('webpack.config.js'); + const defaultConfigPath = fs.embarkPath('webpack.config.js'); - function runWebpack(next) { - self.webpackRun(file.filename, {}, true, importsList, true, realCwd, next); - }, - - function changeCwdBack(next) { - process.chdir(realCwd); - next(); + let config, configPath; + try { + if (fs.existsSync(dappConfigPath)) { + delete require.cache[dappConfigPath]; + configPath = dappConfigPath; + config = require(dappConfigPath); + } else { + configPath = defaultConfigPath; + config = require(defaultConfigPath); } - ], (err) => { - process.chdir(realCwd); - callback(err); - }); - } + // valid config types: https://webpack.js.org/configuration/configuration-types/ + // + function that returns a config object + // + function that returns a promise for a config object + // + array of named config objects + // + config object + if (typeof config === 'function') { + config = await config(self.webpackConfigName); + } else if (Array.isArray(config)) { + config = config.filter(cfg => cfg.name === self.webpackConfigName); + if (!config.length) { + const errMsg = `no webpack config has the name '${self.webpackConfigName}'`; + console.error(errMsg); + return callback(errMsg); + // ?? should the message be wrapped in new Error() + } + if (config.length > 1) { + console.warn( + `detected ${config.length} webpack configs having the name '${self.webpackConfigName}', using the first one` + ); + } + config = config[0]; + } else { + // proceed with the value obtained from require(dapp/default/Config) + } + } catch (e) { + console.error(`error while loading webpack config ${configPath}`); + console.error(e.message); + callback(e); + // ?? should return e or e.message + } - webpackRun(filename, options, includeModules, importsList, detectErrors, realCwd, callback) { - const self = this; - glob(fs.dappPath('.embark/versions/*/*'), (err, files) => { - let versions; + if (typeof config !== 'object' || config === null) { + const errMsg = 'bad webpack config, the resolved config was null or not an object'; + console.error(errMsg); + return callback(errMsg); + // ?? should the message be wrapped in new Error() + } + + webpack(config).run(async (err, stats) => { if (err) { console.error(err); - versions = []; - } else { - versions = files; + return callback(err); } - let defaultOptions = { - mode: self.env === 'production' ? 'production' : 'none', - // devtool: self.env === 'development' ? 'source-map' : false, - // pipeline would need to copy .map files to dist/ target dir - // note: generating full source maps ('source-map') roughly doubles build time - entry: fs.dappPath(filename), - output: { - globalObject: 'typeof self !== \'undefined\' ? self : this', - libraryExport: 'default', - libraryTarget: 'umd', - path: fs.dappPath('.embark'), - filename: filename, - umdNamedDefine: true - }, - // profile: true, - // stats: 'verbose', - // note: generating and writing to disk verbose stats increases build time - resolve: { - alias: importsList, - modules: [ - fs.dappPath('node_modules'), - ...versions, - fs.embarkPath('node_modules') - ] - }, - plugins: [ - new HardSourceWebpackPlugin({ - cacheDirectory: fs.dappPath('node_modules/.cache/hard-source'), - // ufglify (wp mode: production) will still save its cache in embark's node_modules/.cache/ - environmentHash: { - root: fs.dappPath() - } - }), - new HardSourceWebpackPlugin.ExcludeModulePlugin( - [{test: /app[\\/]|contracts[\\/]/}] - ) - ] - }; - - let webpackOptions = utils.recursiveMerge(defaultOptions, options); - - if (includeModules) { - webpackOptions.module = { - rules: [ - { - test: /\.css$/, - use: [{loader: "style-loader"}, {loader: "css-loader"}] - }, - { - test: /\.scss$/, - use: [{loader: "style-loader"}, {loader: "css-loader"}] - }, - { - test: /\.(png|woff|woff2|eot|ttf|svg)$/, - loader: 'url-loader?limit=100000' - }, - { - test: /\.js$/, - loader: "babel-loader", - exclude: /(node_modules|bower_components|\.embark[\\/]versions)/, - options: { - presets: [ - [ - "@babel/preset-env", { - modules: false, - targets: { - browsers: ["last 1 version", "not dead", "> 0.2%"] - } - } - ], - "@babel/preset-react" - ].map(pkg => { - if (Array.isArray(pkg)) { - let _pkg = pkg[0]; - pkg[0] = require.resolve(_pkg); - return pkg; - } else { - return require.resolve(pkg); - } - }), - plugins: [ - "babel-plugin-webpack-aliases", - [ - "@babel/plugin-transform-runtime", { - corejs: 2, - useESModules: true - } - ] - ].map(pkg => { - if (Array.isArray(pkg)) { - let _pkg = pkg[0]; - pkg[0] = require.resolve(_pkg); - return pkg; - } else { - return require.resolve(pkg); - } - }), - compact: false - } - } - ] - }; - - let dappBabelrc = path.join(realCwd, '.babelrc'); - if (fs.existsSync(dappBabelrc)) { - webpackOptions.module.rules[3].options.extends = dappBabelrc; + try { + if (config.stats && config.stats !== 'none') { + self._log('info', 'writing file '+ ('.embark/stats.report').bold.dim); + await writeFile( + fs.dappPath('.embark/stats.report'), + stats.toString(config.stats) + ); + self._log('info','writing file '+ ('.embark/stats.json').bold.dim); + await writeFile( + fs.dappPath('.embark/stats.json'), + JSON.stringify(stats.toJson(config.stats)) + ); } + } catch (e) { + console.error(e.message); + return callback(err); } - - webpack(webpackOptions).run((err, stats) => { - async.waterfall([ - function checkStatsError(next) { - if (err) { - console.error(err); - return next(err); - } - if (!detectErrors) { - return next(); - } - if (stats.hasErrors()) { - return next( - stats.toJson(webpackOptions.stats).errors.join("\n") - ); - } - next(); - }//, - // function writeStatsReport(next) { - // if (detectErrors) { - // self._log('info', 'writing file '+ ('.embark/stats.report').bold.dim); - // } - // fs.writeFile( - // path.join(fs.dappPath('.embark'), 'stats.report'), - // stats.toString(webpackOptions.stats), - // next - // ); - // }, - // function writeStatsJSON(next) { - // if (detectErrors) { - // self._log('info','writing file '+ ('.embark/stats.json').bold.dim); - // } - // fs.writeFile( - // path.join(fs.dappPath('.embark'), 'stats.json'), - // JSON.stringify(stats.toJson(webpackOptions.stats)), - // next - // ); - // } - // note: to visualize the stats info in a browser, do... - // `npx webpack-bundle-analyzer /.embark/stats.json` - ], (err) => { - callback(err); - }); - }); + if (config.stats && stats.hasErrors()) { + const errors = stats.toJson(config.stats).errors.join('\n'); + console.error(errors); + return callback(errors); + } + callback(); }); } } @@ -214,7 +135,7 @@ process.on('message', (msg) => { } if (msg.action === constants.pipeline.build) { - return webpackProcess.build(msg.file, msg.importsList, (err) => { + return webpackProcess.build(msg.assets, msg.importsList, (err) => { process.send({result: constants.pipeline.built, error: err}); }); }