@toruslabs/torus-scripts
Version:
Scripts for Torus libraries to manage bundling, lint etc
212 lines (191 loc) • 6.47 kB
JavaScript
// gets the webpack config for build
// merges the user provided config with the default config
// and returns the merged config
// By default this generates umd builds
import merge from "lodash.mergewith";
import path from "path";
import fs from "fs";
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
import webpack from "webpack";
import { createRequire } from "node:module";
import ESLintPlugin from "eslint-webpack-plugin";
import mergeWith from "lodash.mergewith";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
const require = createRequire(import.meta.url);
import paths, { appModuleFileExtensions } from "./paths.js";
import babelConfig from "./babel.config.js";
import torusConfig from "./torus.config.js";
import { readFile } from "../helpers/utils.js";
import tsconfigBuild from "./tsconfig.build.js";
const { appWebpackConfig, appBuild } = paths;
const { NODE_ENV = "production" } = process.env;
const configImported = (await readFile(appWebpackConfig)).default || { baseConfig: {} };
const babelLoaderOptions = {
...babelConfig,
babelrc: false,
configFile: false,
cacheDirectory: true,
cacheCompression: false,
};
if (fs.existsSync(paths.appBrowserslistConfig)) {
babelLoaderOptions.browserslistConfigFile = paths.appBrowserslistConfig;
} else {
babelLoaderOptions.targets = torusConfig.browserslistrc;
}
export const babelLoader = {
test: /\.(ts|js)x?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: babelLoaderOptions,
},
};
const polyfillPlugins = [
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
}),
new webpack.ProvidePlugin({
process: "process/browser.js",
}),
];
export const resolveWebpackModule = (localPath) => {
if (fs.existsSync(path.resolve(paths.ownNodeModules, localPath))) return new URL(paths.ownNodeModules + "/" + localPath, import.meta.url).pathname;
else if (fs.existsSync(path.resolve(paths.appNodeModules, localPath)))
return new URL(paths.appNodeModules + "/" + localPath, import.meta.url).pathname;
return require.resolve(localPath);
};
const polyfillFallback = mergeWith(
{
http: resolveWebpackModule("stream-http/index.js"),
https: resolveWebpackModule("https-browserify/index.js"),
os: resolveWebpackModule("os-browserify/browser.js"),
crypto: resolveWebpackModule("crypto-browserify/index.js"),
assert: resolveWebpackModule("assert/build/assert.js"),
stream: resolveWebpackModule("stream-browserify/index.js"),
url: resolveWebpackModule("url/url.js"),
buffer: resolveWebpackModule("buffer/index.js"),
zlib: resolveWebpackModule("browserify-zlib/lib/index.js"),
fs: false,
path: false,
},
torusConfig.polyfillNodeDeps,
(objValue, srcValue) => {
if (srcValue === true) return objValue;
if (typeof srcValue === "string") return srcValue;
return undefined;
},
);
function generateLibraryName(pkgName) {
return pkgName.charAt(0).toUpperCase() + pkgName.slice(1);
}
// objValue is the first object (our default config)
function customizer(objValue, srcValue, key) {
// merge plugins if they are not there
if (Array.isArray(objValue) && (key === "plugins" || key === "rules")) {
return [...objValue, ...(srcValue || [])];
}
}
export default (pkgName) => {
const { baseConfig: userBaseConfig, ...rest } = configImported;
// create a copy of baseConfig every time so that loaders use new instances
const umdConfig = merge(
getDefaultUmdConfig(pkgName),
merge(getDefaultBaseConfig(pkgName), userBaseConfig, customizer),
rest.umdConfig || {},
customizer,
);
const finalConfigs = [];
if (torusConfig.umd) finalConfigs.push(umdConfig);
delete rest.umdConfig;
return [
...finalConfigs,
...Object.values(rest || {}).map((x) => {
const baseConfig = merge(getDefaultBaseConfig(pkgName), userBaseConfig, customizer);
return merge(baseConfig, x, customizer);
}),
];
};
export const getDefaultBaseConfig = () => {
return {
mode: NODE_ENV,
devtool: NODE_ENV === "development" ? "eval" : false,
entry: paths.appIndexFile,
target: "web",
output: {
path: appBuild,
library: {
// Giving a name to builds aggregates all exports under that name
// name: generateLibraryName(pkgName)
},
},
resolve: {
extensions: appModuleFileExtensions.map((x) => `.${x}`),
alias: {
"bn.js": require.resolve("bn.js/lib/bn.js"),
// lodash: require.resolve("lodash/index.js"),
},
},
plugins: [new webpack.IgnorePlugin({ resourceRegExp: /^\.\/wordlists\/(?!english)/, contextRegExp: /bip39\/src$/ })],
module: {
rules: [babelLoader],
},
node: {},
};
};
export const getDefaultUmdConfig = (pkgName) => {
const plugins = [];
if (tsconfigBuild.compilerOptions.plugins && tsconfigBuild.compilerOptions.plugins.length > 0) {
const isTransformPlugin = tsconfigBuild.compilerOptions.plugins.some((x) => x.transform === "typescript-transform-paths");
if (isTransformPlugin) {
plugins.push(
new ForkTsCheckerWebpackPlugin({
typescript: {
mode: "readonly",
context: paths.appPath,
configFile: paths.appTsBuildConfig,
typescriptPath: require.resolve("ts-patch/compiler"),
},
}),
);
}
}
if (NODE_ENV === "production") {
plugins.push(
new ESLintPlugin({
context: paths.appPath,
threads: !process.env.CI,
configType: "flat",
extensions: ["ts", "tsx"],
emitError: true,
emitWarning: true,
failOnError: process.env.NODE_ENV === "production",
cache: true,
cacheLocation: path.resolve(paths.appNodeModules, ".cache/.eslintcache"),
}),
);
}
return {
output: {
filename: `${pkgName}.umd.min.js`,
library: {
type: "umd",
// Giving a name to builds aggregates all exports under that name
name: generateLibraryName(pkgName),
},
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new BundleAnalyzerPlugin({
analyzerMode: torusConfig.analyzerMode,
openAnalyzer: false,
}),
...polyfillPlugins, // always polyfill buffer and process
...plugins,
],
resolve: {
fallback: polyfillFallback,
},
};
};