exports_resolve.js

/**
 * @file exports/resolve.js
 * @copyright @spmhome @_2025
 * @author Scott Meesseman @spmeesseman
 *
 * @see {@link https://webpack.js.org/configuration/resolve webpack.js.org/resolve}
 *
 *//** */

// const { ProvidePlugin } = require("webpack");
const WpwWebpackExport = require("./base");


/**
 * @augments WpwWebpackExport
 */
class WpwResolveExport extends WpwWebpackExport
{
	/**
	 * @private
	 */
	static polyfills = {
		buffer: "buffer", console: "console-browserify", constants: "constants-browserify", crypto: "crypto-browserify",
		events: "events", http: "http-browserify", https: "https-browserify", os: "os-browserify", path: "path-browserify",
		querystring: "querystring", stream: "stream-browserify", tty: "tty-browserify", url: "url-browserify", util: "util",
		vm: "vm-browserify", zlib: "zlib-browserify"
	};


	/**
     * @param {WpwExportOptions} options Plugin options to be applied
     */
	constructor(options)
	{
		super(options);
        this.buildOptions = /** @type {WpwBuildOptionsExportConfig<"resolve">} */(this.buildOptions);
	}


	/**
     * @override
     * @param {WpwBuild} b
     */
	static create = (b) => WpwResolveExport.wrap.call(this, b, b.isAnyAppOrLib || b.isTypes || b.isResource || b.isTests);


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	app(build)
	{
		this.applyPolyfills(build);
		build.wpc.resolve.mainFields = build.isModule ? [ "exports", "module", "main" ] : [ "exports", "main", "module" ];
		if (!build.isWeb) {
			// if (build.pkgJson.exports) {
				// build.wpc.resolveLoader.conditionNames = [  "node", "types", "require", "import", "default" ];
			// }
			// build.wpc.resolveLoader.exportsFields = [ "exports" ];
			// build.wpc.resolve.importsFields = [ ".", "node", "nodejs", "import", "require", "types", "default" ];
		}
		else {
			// if (build.pkgJson.exports) {
				// build.wpc.resolve.conditionNames = [ "browser", "web" ];
			// }
			/// build.wpc.resolveLoader.exportsFields = [  "browser", "types", "require", "import", "default" ];
			// build.wpc.resolve.mainFields = build.isModule ? [ "browser", "module", "main" ] : [ "browser", "main", "module" ]
			build.wpc.resolve.mainFields.unshift("browser");
		}
	}


	/**
     * @param {WpwBuild} build
	 */
	applyPolyfills(build)
	{
		const poly = Object.entries(this.buildOptions.polyfill || {});
		build.wpc.resolve.fallback = build.isWeb ? { fs: false } : (poly.length > 0 ? {} : undefined);
		if (poly.length > 0)
		{   poly.filter(([ _, p ]) => this._types_.isString(p, true))
				.map(([ m, p ]) => ([ m, build.getBasePath({ path: p }) ]))
				.filter(([ _, p ]) => this._fs_.existsSync(p))
				.forEach(([ m, p ]) => { build.wpc.resolve.fallback[m] = p; });
			poly.filter(([ m, b ]) => this._types_.isBoolean(b) && WpwResolveExport.polyfills[m])
			    .forEach(([ m ]) => { build.wpc.resolve.fallback[m] = WpwResolveExport.polyfills[m]; });
			// this._obj_.apply(build.wpc.resolve.fallback,
			// {   fs: !web,
			// 	util: cfgPoly.util === true ? require.resolve("util") : false,
			// 	vm: cfgPoly.vm === true ? require.resolve("vm-browserify") : false,
			// 	tty: cfgPoly.tty === true ? require.resolve("tty-browserify") : false,
			// 	url: cfgPoly.url === true ? require.resolve("url-browserify") : false,
			// 	path: cfgPoly.path === true ? require.resolve("path-browserify") : false,
			// 	http: cfgPoly.http === true ? require.resolve("http-browserify") : false,
			// 	zlib: cfgPoly.zlib === true ? require.resolve("zlib-browserify") : false,
			// 	os: cfgPoly.os === true ? require.resolve("os-browserify") : false,
			// 	events: cfgPoly.events === true ? require.resolve("events") : false,
			// 	https: cfgPoly.https === true ? require.resolve("https-browserify") : false,s
			// 	crypto: cfgPoly.crypto === true ? require.resolve("crypto-browserify") : false,
			// 	stream: cfgPoly.stream === true ? require.resolve("stream-browserify") : false,
			// 	console: cfgPoly.console === true ? require.resolve("console-browserify") : false,
			// 	querystring: cfgPoly.querystring === true ? require.resolve("querystring") : false,
			// 	constants: cfgPoly.constants === true ? require.resolve("constants-browserify") : false,
			// 	buffer: cfgPoly.buffer === true || cfgPoly.crypto === true ? require.resolve("buffer") : false
			// 	// vm: cfgPoly.vm === true || cfgPoly.buffer === true ? require.resolve("vm-browserify") : false,
			// });
			Object.entries(build.wpc.resolve.fallback).forEach(([ p, r ]) => { build.wpc.resolve.alias[p] = r; });
			if (this._types_.isObjectEmpty(build.wpc.resolve.fallback)) {
				this._obj_.cleanPrototype(build.wpc.resolve, "fallback");
			}
			// build.wpc.plugins.push(new ProvidePlugin({ process: "process/browser", Buffer: [ "buffer", "Buffer" ] }));s
		}
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	base(build)
	{
		const joinPath = this._path_.joinPath,
			  modules = [ "node_modules", ...build.nodeModulesPaths.all ];

		build.logger.write("initialize webpack 'resolve' configuration", 1);
		build.logger.value("   node_modules path", build.nodeModulesPaths.base, 1);
		build.logger.value("   context node_modules path", build.nodeModulesPaths.ctx, 2);
		build.logger.value("   global node_modules path", build.nodeModulesPaths.global, 2);
		build.logger.value("   wpw node_modules path", build.nodeModulesPaths.wpw, 2);

		this._obj_.apply(this.build.wpc, /** @type {Partial<WpwWebpackConfig>} */(
		{   resolve:
			{   modules: [ ...modules, this._path_.resolvePath(build.nodeModulesPaths.wpw, "..") ],
				extensions: [ ".js", ".cjs", ".mjs" ],
				cache: !!((build.options.cache?.type === "filesystem") && (build.options.cache?.resolver))
			},
			resolveLoader:
			{   modules: [
					!this.global.isDevExec ? joinPath(build.wpwPath, "loaders") : joinPath(build.wpwPath, "src", "loaders"),
					...modules, this._path_.resolvePath(build.nodeModulesPaths.wpw, "..")
				]
			}
		}));

		if (build.isTranspiled)
		{   const compilerOptions = build.tsc.compilerOptions;
			if (build.isTs || build.isTsJs || build.isJsTs)
			{   build.wpc.resolve.extensions.unshift(".ts", ".tsx");
				build.wpc.resolve.extensionAlias = {
					".js": [ ".js", ".ts" ], ".cjs": [ ".cjs",".cts" ], ".mjs": [ ".mjs",".mts" ]
				};
			}
			build.wpc.resolve.alias = this.resolveAliasPaths(build, build.nodeModulesPaths.base);
			if (compilerOptions.jsx) {
				this._arr_.pushUniq2(build.wpc.resolve.extensions, this.global.isWin32, ".jsx");
			}
			if (compilerOptions.resolveJsonModule) {
				this._arr_.pushUniq2(build.wpc.resolve.extensions, this.global.isWin32, ".json");
			}
		}
	}


	/**
	 * @override
	 */
	doxygen() {}


	/**
	 * @override
	 */
	extjsdoc() {}


	/**
	 * @override
     * @param {WpwBuild} _build
	 */
	jsdoc(_build) {}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	lib(build) { this.app(build); }


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	plugin(build) { this.app(build); }


    /**
     * @private
     * @param {WpwBuild} build
     * @param {string} modulesPath
	 * @returns {WpwWebpackAliasConfig}
     */
    resolveAliasPaths(build, modulesPath)
    {
		const tsConfig = build.tsconfig,
			  existsSync = this._fs_.existsSync,
              tscfgSrcDir = build.source.info.dir,
		      resolvePath = this._path_.resolvePath,
              tscfgPaths = tsConfig.compilerOptions.paths;

        /** @type {WpwWebpackAliasConfig} */
		const alias = {
			"append-transform": resolvePath(modulesPath, "append-transform/index.js"),
			// "fsevents": resolvePath(this.nodeModulesPath, "fsevents/index.js")
			"./module-types": resolvePath("../../@babel/core/lib/config/files/module-types.js")
			// "@babel/preset-env": resolvePath(build.nodeModulesPaths.wpw, "@babel/preset-env/lib/index.js/"),
			// "@babel/preset-module": resolvePath(build.nodeModulesPaths.wpw, "@babel/preset-module/lib/index.js/"),
			// "@babel/preset-typescript": resolvePath(build.nodeModulesPaths.wpw, "@babel/preset-typescript/lib/index.js/")
		};

        if (tscfgSrcDir && tscfgPaths)
        {
            Object.entries(tscfgPaths).filter(([ _, p ]) => this._types_.isArray(p)).forEach(([ key, p ]) =>
            {   alias[key.replace(/[/\\]\*+$/, "")] = p.map(
				    (p) => resolvePath(tscfgSrcDir, p).replace(/[/\\]\*+$/, "")).filter((p) => existsSync(p)
				);
            });
        }

		return alias;
    }


	/**
	 * @override
     * @param {WpwBuild} _build
	 */
	schema(_build) {}


	/**
	 * @override
     * @param {WpwBuild} _build
	 */
	script(_build) {}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	resource(build) { this._arr_.pushUniq(/** @type {string[]} */(build.wpc.resolve.extensions), ".json"); }


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	tests(build)
	{
		const spmTestUtilsPath =  this._path_.resolvePath(build.getBasePath(), "node_modules/@spmhome/test-utils/node_modules");
		this.applyPolyfills(build);
		if (this._fs_.existsSync(spmTestUtilsPath)) {
			build.wpc.resolve.modules.unshift(spmTestUtilsPath);
		}
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	types(build)
	{
		this.applyPolyfills(build);
		this._arr_.pushUniq2(build.wpc.resolve.extensions, this.global.isWin32, ".d.ts", ".ts");
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	webapp(build)
	{
		this.applyPolyfills(build);
		this._arr_.pushUniq2(build.wpc.resolve.extensions, this.global.isWin32, ".scss", ".woff", ".woff2");
	}
};


module.exports = WpwResolveExport.create;