exports_output.js

/**
 * @file src/exports/output.js
 * @copyright @spmhome @_2025
 * @author Scott Meesseman @spmeesseman
 *
 * @see {@link [webpack.js.org/output](https://webpack.js.org/configuration/output)}
 *
 *//** */

const { TestsChunk } = require("../utils/regex");
const WpwWebpackExport = require("./base");


 /**
  * @augments WpwWebpackExport
  */
 class WpwOutputExport extends WpwWebpackExport
 {
	/**
     * @param {WpwExportOptions} options
     */
	constructor(options)
	{
		super(options);
        this.buildOptions = /** @type {WpwBuildOptionsExportConfig<"output">} */(this.buildOptions); // reset for typings
	}

	/**
     * @override
     */
	static create = WpwOutputExport.wrap.bind(this);


    /**
     * @override
     * @param {WpwBuild} build
     */
    app(build)
    {
		const name = "[name]",
			  ext = this.outputExt,
			  hashEnabled = !!build.options.hash?.enabled;

		build.logger.write("output rc configuration:", 1);
		build.logger.values([
			[ "extension", ext ], [ "filename", `${name}${ext}` ], [ "output path", build.wpc.output.path ]
		], 1, "   ");

		this._obj_.apply(build.wpc.output,
		{
			filename: (/** @type {WebpackPathDataOutput} */data, /** @type {WebpackAssetInfo | undefined} */_info) =>
			{
				const hashedName = hashEnabled && !TestsChunk.test(data.chunk.name || "");
				return `${name}${hashedName ? ".[contenthash]" : ""}${ext}`;
			}
		});

		if (hashEnabled && (build.options.hash.emitNoHash || build.options.hash.emitAppNoHash))
		{
			build.logger.write("no-hash copy option enabled, reset output path to virtual", 3);
			build.logger.value("   new path", build.virtualEntry.dirBuild,  3);
			this._obj_.apply(build.wpc.output, { path: build.virtualEntry.dirBuild });
		}
    }


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	base(build)
	{
		this._obj_.apply(build.wpc.output,
		{
			filename: "[name].js",
			iife: !build.isModule,
			module: build.isModule,
			compareBeforeEmit: true,
			path: build.getDistPath(),
			hashDigest: this.buildOptions.hashDigest,
			hashFunction: this.buildOptions.hashFunction,
			chunkFormat: build.isModule ? "module" : undefined,
			hashDigestLength: this.buildOptions.hashLength || 16,
			libraryTarget: this.getLibraryType() // || "commonjs2"
		});
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	baseDone(build)
	{
		// if (!build.isWeb || build.wpc.output.libraryTarget === "umd") {
		if (build.wpc.output.libraryTarget === "umd") {
			build.wpc.output.globalObject = "this";
		}
		else if (build.isWebApp || build.isWebWorker) {
			build.wpc.output.globalObject = "self";
		}
		else if (build.isWeb) {
			build.wpc.output.globalObject = "globalThis";
		}
		else {
			build.wpc.output.globalObject = "global";
		}
	}


	/**
	 * @private
     * @param {WpwBuild} build
	 */
	docs(build)
	{
        if (build.options.doc.output)
        {
			const o = build.wpc.output;
            o.path = o.path.replace(new RegExp(`\\/${this._rgx_.escapeRegExp(build.options.doc.output)}$`), "");
        }
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	doxygen(build)
	{
        this.docs(build);
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	extjsdoc(build)
	{
        this.docs(build);
	}


	/**
	 * @private
	 * @returns {WebpackLibrary}
	 * @throws {SpmhError}
	 */
	getLibraryType()
	{
		const b = this.build;
		/** @type {WebpackLibrary} */
		let libType = "global";
		if (b.isAnyAppOrLib)
		{
			const checkTypes = [ "amd", "umd", "jsonp", "system" ];
			if (checkTypes.includes(this.build.wpc.externalsType) && !checkTypes.includes(libType))
			{
				throw this.addMessage({
					code: this.MsgCode.ERROR_CONFIG_INVALID_EXPORTS,
					message: `library type (${libType}) must be == externals type '${this.build.wpc.externalsType}' `
				});
			}
			if (checkTypes.includes(this.build.wpc.externalsType))
			{
				b.logger.write(`set output library type == externals type '${libType}'`, 2);
				libType = /** @type { "amd" | "umd" | "jsonp" | "system"} */(this.build.wpc.externalsType);
				b.library = libType;
			}
			else // if (!isWebpackLibrary(libType))
			{
				libType = b.library || "commonjs2";
				b.logger.write(`set output library type to '${libType}'`, 2);
			}
		}
		return libType;
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	jsdoc(build)
	{
		this.docs(build);
	}


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	lib(build)
	{
		this.app(build);
		this._obj_.apply(this.build.wpc.output,
		{
			enabledLibraryTypes: [
				this.build.wpc.output.libraryTarget
			],
			library: {
				type: this.build.wpc.output.libraryTarget
			}
		});
	}


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


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


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


	/**
	 * @override
     * @param {WpwBuild} build
	 */
	script(build)
	{
		if (build.options.script.emitType === "vbuild" || build.options.script.emitType === "vdist") {
			this._obj_.apply(build.wpc.output, { path: build.virtualEntry.dirBuild });
		}
	}


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


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


    /**
     * @override
     * @param {WpwBuild} build
     */
    webapp(build)
    {
		if (build.options.hash?.enabled && (build.options.hash.emitNoHash || build.options.hash.emitAppNoHash))
		{
			build.wpc.output.path = build.virtualEntry.dirBuild;
		}
        this._obj_.apply(build.wpc.output,
		{
			// clean: build.clean ? { keep: /(img|font|readme|walkthrough)[\\/]/ } : undefined,
			publicPath: process.env.AP_WWW_ASSET_PATH || process.env.AP_WWW_PUBLIC_PATH || "/public",
			filename: (/** @type {WebpackPathDataOutput} */data, /** @type {WebpackAssetInfo | undefined} */_info) =>
			{
				let name = `${data.chunk.name}${build.options.hash?.enabled ? ".[contenthash]" : ""}`;
				if (build.options.web?.camelToDash)
				{
					name = name.replace(/[a-z]+([A-Z])/g, (substr, token) => substr.replace(token, "-" + token.toLowerCase()));
					build.logger.write(
					`   convert asset name to '${name}' [from ${data.chunk.name}]`, 2
					);
				}
				return `${build.options.web?.jsDirectory ? "js/" : ""}${name}${this.outputExt}`;
			}
		});

		if (build.options.web?.publicPath)
		{
			build.logger.write(`   set rc configured publicPath to '${build.options.web.publicPath}'`, 3);
			if (this._types_.isString(build.wpc.output.publicPath) && !build.wpc.output.publicPath.endsWith("/")) {
				build.wpc.output.publicPath += "/";
			}
			build.wpc.output.publicPath = build.options.web.publicPath;
		}
		else if (build.options.vsceiso?.enabled)
		{
			build.logger.write("   set publicPath to '#{webroot}/' for vsc build", 3);
			build.wpc.output.publicPath = "#{webroot}/";
		}
    }
 }


module.exports = WpwOutputExport.create;