Created
February 23, 2021 06:22
-
-
Save Luiz-Monad/c64dddb988b4a85a8a6736ae9aa1f5ab to your computer and use it in GitHub Desktop.
webpack from hell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Template for webpack.config.js in Fable projects | |
// Find latest version in https://github.com/fable-compiler/webpack-config-template | |
// In most cases, you'll only need to edit the CONFIG object (after dependencies) | |
// See below if you need better fine-tuning of Webpack options | |
// Dependencies. Also required: core-js, fable-loader, fable-compiler, @babel/core, @babel/preset-env, babel-loader | |
const webpack = require('webpack'); | |
//const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; | |
const HtmlWebpackPlugin = require('html-webpack-plugin'); | |
const util = require('./webpack.util'); | |
const path = require('path'); | |
const localResolve = util.resolve(__dirname); | |
const configFn = paramFile => { | |
const options = require(paramFile); | |
const resolve = util.resolve(options.dirName); | |
const baseConfig = { | |
dirName: options.dirName, | |
fsharpDefine: options.fsharpDefine, | |
fsharpEntry: resolve(options.fsharpEntry), | |
indexHtmlTemplate: [ | |
'raw-loader', | |
localResolve('webpack.loader.node.js'), | |
resolve(path.join(options.outputDir, '../ssr/ssr.js')) | |
].join('!'), | |
srcDir: resolve('.'), | |
nodeModDir: resolve('node_modules'), | |
outputDir: resolve(options.outputDir), | |
assetsDir: resolve(options.assetsDir), | |
devServerHost: options.devServerHost, | |
devServerPort: options.devServerPort, | |
// When using webpack-dev-server, you may need to redirect some calls | |
// to a external API server. See https://webpack.js.org/configuration/dev-server/#devserver-proxy | |
devServerProxy: { | |
// redirect requests that start with /api/* to the server on port port | |
'/api/*': { | |
target: 'http://localhost:' + options.devServerProxy, | |
changeOrigin: true | |
}, | |
// redirect websocket requests that start with /socket/* to the server on the port 8085 | |
'/socket/*': { | |
target: 'http://localhost:' + options.devServerProxy, | |
ws: true | |
} | |
}, | |
// Use babel-preset-env to generate JS compatible with most-used browsers. | |
// More info at https://babeljs.io/docs/en/next/babel-preset-env.html | |
babel: { | |
presets: [ | |
['@babel/preset-env', { | |
modules: false, | |
// This adds polyfills when needed. Requires core-js dependency. | |
// See https://babeljs.io/docs/en/babel-preset-env#usebuiltins | |
// Note that you still need to add custom polyfills if necessary (e.g. whatwg-fetch) | |
useBuiltIns: 'usage', | |
corejs: 3, | |
}] | |
], | |
} | |
}; | |
// If we're running the webpack-dev-server, assume we're in development mode | |
const isProduction = (process.argv.mode === 'production'); | |
const mode = (isProduction ? 'production' : 'development'); | |
console.log('Bundling for ' + mode + '...'); | |
// The HtmlWebpackPlugin allows us to use a template for the index.html page | |
// and automatically injects <script> or <link> tags for generated bundles. | |
const commonPlugins = [ | |
new HtmlWebpackPlugin({ | |
filename: 'index.html', | |
template: baseConfig.indexHtmlTemplate, | |
inlineSource: '^.+\.(css)$', | |
}), | |
//new BundleAnalyzerPlugin(), | |
]; | |
const thisConfig = { | |
// In development, split the JavaScript files in order to | |
// have a faster HMR support. | |
entry: { | |
app: [ | |
baseConfig.fsharpEntry | |
] | |
}, | |
// Add a hash to the output file name in production | |
// to prevent browser caching if code changes | |
output: { | |
path: baseConfig.outputDir, | |
pathinfo: !isProduction, | |
chunkFilename: isProduction ? '[name].[chunkhash].js' : '[name].js', | |
filename: isProduction ? '[name].[chunkhash].js' : '[name].js', | |
}, | |
mode: mode, | |
devtool: isProduction ? 'source-map' : 'eval-source-map', //'inline-cheap-source-map', | |
context: baseConfig.srcDir, | |
optimization: { | |
minimize: isProduction, | |
nodeEnv: mode, | |
splitChunks: { | |
chunks: 'all', | |
name(module, chunks, cacheGroupKey) { | |
const allChunksNames = chunks.map((item) => item.name).join('~'); | |
return `${cacheGroupKey}-${allChunksNames}`; | |
}, | |
cacheGroups: { | |
vendors: { | |
test: /[\\/]node_modules[\\/]/, | |
priority: -10, | |
reuseExistingChunk: true, | |
}, | |
fable: { | |
test: /[\\/].fable[\\/]/, | |
priority: -20, | |
enforce: true | |
}, | |
default: { | |
test: /[\\/]src[\\/]/, | |
priority: -20, | |
enforce: true | |
}, | |
}, | |
}, | |
}, | |
cache: { | |
type: 'filesystem' | |
}, | |
target: 'web', | |
// Besides the HtmlPlugin, we use the following plugins: | |
// PRODUCTION | |
// DEVELOPMENT | |
// - HotModuleReplacementPlugin: Enables hot reloading when code changes without refreshing | |
plugins: isProduction | |
? commonPlugins.concat([ | |
]) | |
: commonPlugins.concat([ | |
new webpack.HotModuleReplacementPlugin(), | |
]), | |
resolve: { | |
// See https://github.com/fable-compiler/Fable/issues/1490 | |
symlinks: false, | |
modules: [ | |
baseConfig.srcDir, | |
baseConfig.nodeModDir, | |
baseConfig.assetsDir, | |
baseConfig.outputDir, | |
] | |
}, | |
// Configuration for webpack-dev-server | |
devServer: { | |
publicPath: '/', | |
contentBase: baseConfig.assetsDir, | |
host: baseConfig.devServerHost, | |
port: baseConfig.devServerPort, | |
proxy: baseConfig.devServerProxy, | |
hot: true, | |
inline: true, | |
}, | |
// - cache-loader: caches transformed files. | |
// - fable-loader: transforms F# into JS. | |
// - babel-loader: transforms JS to old syntax (compatible with old browsers). | |
// - file-loader: moves files referenced in the code (fonts, images) into output folder. | |
// - css-loader: parses css, transforms imports to JS requires. | |
// - html-loader: parses html, transforms srcs/hrefs to JS requires. | |
// - extract-loader: requires resources as dependencies (from css/html loaders). | |
// - raw-loader: transforms previous text resource into a JS module. | |
// - val-loader: runs external commands to generate assets. | |
module: { | |
rules: [ | |
{ | |
test: /\.fs(x|proj)?$/, | |
use: [{ | |
loader: 'fable-loader', | |
options: { | |
babel: baseConfig.babel, | |
define: baseConfig.fsharpDefine.concat( | |
isProduction ? [] : ["DEBUG"]), | |
extra: { optimizeWatch: !isProduction } | |
} | |
}] | |
}, | |
{ | |
test: /\.js$/, | |
exclude: /node_modules/, | |
use: [{ | |
loader: 'babel-loader', | |
options: baseConfig.babel, | |
}] | |
}, | |
{ | |
type: 'javascript/auto', | |
test: /\.json$/, | |
use: [{ | |
loader: 'file-loader', | |
}] | |
}, | |
{ | |
test: /\.css$/, | |
use: [{ | |
loader: 'file-loader', | |
}, { | |
loader: 'extract-loader', | |
}, { | |
loader: 'css-loader', | |
}] | |
}, | |
{ | |
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?.*)?$/, | |
use: [{ | |
loader: 'file-loader', | |
}] | |
}, | |
] | |
}, | |
}; | |
return thisConfig; | |
}; | |
module.exports = env => configFn(env.param); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Config for Node SSR. | |
const webpack = require('webpack'); | |
const baseConfigFn = require('./webpack.config'); | |
const util = require('./webpack.util'); | |
const node_path = require('path'); | |
const configFn = baseConfig => { | |
const isProduction = (baseConfig.mode == 'production'); | |
console.log('Bundling for server-side-rendering...'); | |
// fable-loader extra defines. | |
const loader = util.findLoader(baseConfig.module, 'fable-loader'); | |
const fable = Object.assign({}, loader, | |
{ | |
options: Object.assign({}, loader.options, { | |
define: loader.options.define.concat([ | |
"SSR" | |
]), | |
}) | |
} | |
); | |
return ({ | |
entry: { | |
ssr: baseConfig.entry.app, | |
}, | |
output: { | |
path: node_path.join(baseConfig.output.path, '../ssr/'), | |
pathinfo: baseConfig.output.pathinfo, | |
chunkFilename: '[name].js', | |
filename: '[name].js', | |
libraryTarget: 'umd', | |
}, | |
mode: baseConfig.mode, | |
context: baseConfig.context, | |
optimization: { | |
nodeEnv: baseConfig.mode, | |
minimize: isProduction, | |
removeAvailableModules: false, | |
removeEmptyChunks: false, | |
mergeDuplicateChunks: false, | |
splitChunks: isProduction ? false : baseConfig.optimization.splitChunks, | |
}, | |
cache: { | |
type: 'filesystem' | |
}, | |
target: 'node', | |
plugins: isProduction ? [ | |
new webpack.optimize.LimitChunkCountPlugin({ | |
maxChunks: 1 | |
}), | |
] : [], | |
resolve: { | |
symlinks: baseConfig.symlinks, | |
modules: baseConfig.resolve.modules, | |
alias: { | |
process: "process/browser", | |
crypto: "crypto-browserify", | |
} | |
}, | |
module: util.replaceLoader(baseConfig.module, { | |
'fable-loader': fable | |
}), | |
}); | |
}; | |
module.exports = env => configFn(baseConfigFn(env)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path'); | |
const fs = require('fs'); | |
function processResult(loaderContext, result) { | |
if ( | |
typeof result !== "string" && | |
result instanceof Buffer === false | |
) { | |
loaderContext.callback( | |
new Error( | |
`The returned code of module "${loaderContext.resource}" is neither a string nor an instance of Buffer` | |
) | |
); | |
return; | |
} | |
const dir = path.dirname(loaderContext.resourcePath); | |
const emitFile = files => { | |
if (!files || !files[0]) { | |
loaderContext.cacheable(true); | |
loaderContext.callback( | |
null, | |
result | |
); | |
return; | |
} | |
const file = files[0]; | |
const fullpath = path.join(dir, files[0]); | |
fs.readFile(fullpath, (err, content) => { | |
if (err) { | |
reject(new Error(`Couldn\'t find the "${file}" file.`)); | |
return; | |
} | |
//ignore js files, they are build deps only. | |
if (!(/\.js$/.test(file))) { | |
loaderContext.emitFile(file, content); | |
} | |
loaderContext.addDependency(fullpath); | |
emitFile(files.splice(1)); | |
}); | |
}; | |
fs.readdir(dir, function(err, items) { | |
if (err) { | |
reject(new Error(`Couldn\'t list "${dir}" directory.`)); | |
return; | |
} | |
emitFile(items); | |
}); | |
} | |
const loader = function (content, map, meta) { | |
const callback = this.async(); | |
const exports = require(this.resourcePath); | |
console.log(exports); | |
let func = exports && exports.default ? exports.default : exports; | |
if (typeof func !== "function") { func = exports[Object.keys(exports)[0]] } | |
if (typeof func !== "function") { | |
callback( | |
new Error( | |
`Module "${this.resource}" does not export a function as default` | |
) | |
); | |
return; | |
} | |
let result; | |
try { | |
result = func(); | |
} catch (error) { | |
callback(new Error(`Module "${this.resource}" throw error: ${error}`)); | |
return; | |
} | |
if (result && typeof result.then === "function") { | |
result | |
.then((res) => processResult(this, res)) | |
.catch((error) => { | |
callback(new Error(`Module "${this.resource}" throw error: ${error}`)); | |
}); | |
return; | |
} | |
// No return necessary because processResult calls this.callback() | |
processResult(this, result); | |
} | |
module.exports = loader; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
dirName: __dirname, | |
fsharpDefine: ['PROJ_IS_CLIENT'], | |
fsharpEntry: './src/Client/Client.fsproj', | |
outputDir: './src/Client/out', | |
assetsDir: './src/Client/static', | |
devServerHost: 'localhost', | |
devServerPort: 8086, | |
devServerProxy: 8085, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path'); | |
function resolve(dirname) { | |
return function (filePath) { | |
return path.isAbsolute(filePath) ? filePath : path.join(dirname, filePath); | |
}; | |
} | |
function findLoader(modl, loader) { | |
return modl.rules.map(function(m) { | |
return m.use && m.use.length && m.use[m.use.length - 1].loader === loader | |
? m.use[m.use.length - 1] : null | |
}).filter(function (m) { return !!m })[0]; | |
} | |
function replaceLoader(modl, loaders) { | |
return Object.keys(loaders).reduce(function(prev, cur) { | |
return Object.assign({}, prev, { | |
rules: prev.rules.map(function(m) { | |
return m.use && m.use.length && m.use[m.use.length - 1].loader === cur | |
? Object.assign({}, m, { use: loaders[cur] }) : m | |
}).filter(function (m) { return !!m.use }) | |
}) | |
}, modl); | |
} | |
module.exports = { resolve, findLoader, replaceLoader }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment