roosevelt
Version:
🧸 MVC web framework for Node.js designed to make Express easier to use.
135 lines (120 loc) • 5.03 kB
JavaScript
require('@colors/colors')
const path = require('path')
const fs = require('fs-extra')
module.exports = async app => {
const params = app.get('params')
const logger = app.get('logger')
// use existence of bundles array members to determine if bundler is enabled
if (!params.makeBuildArtifacts) return
if (params.js?.webpack.enable && !params.js.webpack.bundles.length) return
if (params.js?.customBundler.enable && !params.js.customBundler.bundles.length) return
if (params.js.webpack) {
let webpack
if (params.js.webpack) {
try {
webpack = require('webpack')
} catch (err) {
logger.error(`${app.get('appName')} failed to include your Webpack JS bundle! Please ensure Webpack is declared properly in your package.json and that it has been properly installed to node_modules.`)
logger.warn('JS bundling has been disabled')
return
}
}
const TerserPlugin = require('terser-webpack-plugin')
for (const bundle of params.js.webpack.bundles) {
if (bundle.env === params.mode || !bundle.env) {
try {
await (async () => {
let config
if (typeof bundle.config === 'string') {
// process config as a file path
config = require(path.join(app.get('appDir'), bundle.config))
} else {
// process as config object
config = bundle.config
}
if (params.js.webpack.customBundlerFunction) {
await params.js.webpack.customBundlerFunction(bundle, config, app)
} else {
// add dev tools to webpack config if in development mode
if (params.mode === 'development') {
if (!config.mode) config.mode = 'development' // only add this if mode isn't already set in the user's config
if (!config.devtool) config.devtool = 'source-map' // only add this if devtool isn't already set in the user's config
}
if (params.prodSourceMaps) config.devtool = 'source-map' // enable source maps in prod mode if the setting is set
if (!config.optimization) {
config.optimization = {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false // disable LICENSE.txt file generation
})
]
}
}
await new Promise((resolve, reject) => {
webpack(config, (err, stats) => {
if (err) {
reject(err)
return
}
if (stats.hasErrors()) {
reject(stats.toJson().errors[0])
return
}
stats.toJson().assets.map(asset => path.join(config.output.path, asset.name)).forEach(file => logger.info('📝', `${app.get('appName')} writing new JS file ${file}`.green))
resolve()
})
})
}
})()
} catch (err) {
handleBundlingError(err)
}
}
}
} else if (params.js.customBundler) {
for (const bundle of params.js.customBundler.bundles) {
if (bundle.env === params.mode || !bundle.env) {
try {
await (async () => {
let config
if (typeof bundle.config === 'string') {
// process config as a file path
config = require(path.join(app.get('appDir'), bundle.config))
} else {
// process as config object
config = bundle.config
}
if (params.js.customBundler.customBundlerFunction) {
await params.js.customBundler.customBundlerFunction(bundle, config, app)
} else {
throw new Error('No customBundlerFunction defined.')
}
})()
} catch (err) {
handleBundlingError(err)
}
}
}
}
function handleBundlingError (err) {
if (params.js.verbose) {
logger.error('JS bundling error:')
logger.error(err)
if (params.js.verbose === 'file') {
fs.writeFileSync('./jsBundlerError.txt', JSON.stringify(err), err => { if (err) logger.error(err) }) // else file written successfully
}
} else {
logger.error(`JS bundling error: ${err.message}`)
}
}
// check to see if the public folder has .map files in prod mode and warn the user they may want to clear the public folder
if (process.env.NODE_ENV === 'production' && !params.prodSourceMaps) {
for (const file of fs.readdirSync(params.publicFolder, { recursive: true })) {
if (file.endsWith('.map')) {
logger.warn('There are source map (`.map`) files in your public folder. If you do not wish to expose these publicly, then clear your public folder and restart the app in production mode.')
break
}
}
}
}