plugins_release_licensefiles.js
/**
* @file plugin/release/licensefiles.js
* @copyright @spmhome @_2025
* @author Scott Meesseman @spmeesseman
*//** */
const { WebpackTargets } = require("../../utils");
const WpwPlugin = require("../base");
const LicenseWebpackPlugin = require("license-webpack-plugin").LicenseWebpackPlugin;
// class WpwLicenseFilesPlugin extends WpwPlugin
class WpwLicenseFilesPlugin extends WpwPlugin
{
/**
* @type {Record<string, boolean>}
*/
static targetBuilds = {};
/**
* @param {WpwPluginOptions} options
*/
constructor(options)
{
super(options);
WpwLicenseFilesPlugin[`${this.build.target}`] = true;
this.buildOptions = /** @type {WpwBuildOptionsConfig<"licensefiles">} */(this.buildOptions);
}
/**
* @override
* @param {WpwBuild} build
*/
static create = (build) =>
WpwLicenseFilesPlugin.wrap.call(this, build, build.isAnyAppOrLib, !WpwLicenseFilesPlugin[`${build.target}`]);
/**
* @override
* @returns {WpwPluginTapOptions<any, any, any> | void}
*/
onApply()
{
// return apply({}, // super.onApply(),
// {
// combineLicenseFiles: {
// // hook: "compilation",
// hook: "thisCompilation",
// // stage: "ANALYSE",
// stage: "OPTIMIZE_COUNT",
// hookCompilation: "processAssets",
// callback: this.combineLicenseFiles.bind(this)
// }
// });
return {
checkLicenseFiles: {
hook: "compilation",
hookCompilation: "afterProcessAssets",
callback: this.checkLicenseFiles.bind(this)
}
};
}
/**
* @private
* @since 1.8.6
* @param {WebpackCompilationAssets} assets
*/
checkLicenseFiles(assets)
{
const licFile = Object.keys(assets).map((a) => this.compilation.getAsset(a)).find((a) => a?.name.endsWith(".LICENSE"));
if (licFile)
{ this.hookstart();
const b = this.build,
sz = this._types_.isNumber(licFile.info.size) ? licFile.info.size :
(this._types_.isNumber(licFile.source.size()) ? licFile.source.size() : licFile.source.buffer().length);
if (this._types_.isNumber(sz) && sz <= 1)
{
b.logger.write(` bold(delete) empty license-file asset '${licFile.name}'`);
this.compilation.deleteAsset(licFile.name);
}
else
{ let licContent = licFile.source.buffer().toString();
const targets = WebpackTargets.filter((t) => b.buildConfigs.find((b) => b.target === t)),
nms = b.nodeModules.filter((nm) =>
!nm.builtin && !nm.external && !/^\./.test(nm.req) &&
!licContent.includes(`\n\n--- VENDOR | DEPENDENCY [${nm.name}/${licFile.name}]`)
);
b.logger.write(` valid license-file asset '${licFile.name}'`);
for (const nm of nms)
{ if (!licContent.includes(`--- VENDOR | DEPENDENCY [${nm.name}/${licFile.name}]`))
{ const vLic = this._fs_.findExPathSync(
[ "vendor.LICENSE", ...targets.map((t) => `vendor.${t}.LICENSE`) ], [
this._path_.resolvePath(b.nodeModulesPath, nm.name),
...targets.map((t) => this._path_.resolvePath(b.nodeModulesPath, nm.name, `${t}`))
], true);
if (vLic)
{ const vLicContent = this._fs_.readFileSync(vLic);
b.logger.write(` update content with vendor license | '${nm.name}'`);
this.compilation.updateAsset(
licFile.name,
(s) => this.addLicenseToSource(licFile.name, nm.name, vLicContent, s),
this._obj_.clone(licFile.info)
);
licContent += `\n--- VENDOR | DEPENDENCY [${nm.name}/${licFile.name}] ---\n`;
}
}
}
b.logger.value(" combined license-file accumulated size", this._num_.toStorageSize(sz));
}
this.hookdone();
}
}
/**
* @private
* @param {string} file
* @param {string} pkg
* @param {string} license
* @param {WebpackSource} sourceInfo
* @returns {WebpackSource}
*/
addLicenseToSource(file, pkg, license, sourceInfo)
{
const source= sourceInfo.source(),
sep = "".padEnd(31 + pkg.length + file.length, "-"),
rplRgx = new RegExp(`^.*?${this._rgx_.escapeRegExp(license)}.*?[\r\n]`),
src = (source.toString().trim().replace(rplRgx, "") +
`\n\n${sep}\n--- VENDOR | DEPENDENCY [${pkg}/${file}] ---\n${sep}\n\n${license}\n`)
.replace(/(?:\r?\n){3,}/g, "\n\n");
return new this.build.wp.sources.RawSource(src);
}
// /**
// * @private
// * @param {WebpackCompilationAssets} assets
// */
// combineLicenseFiles = (assets) =>
// {
// const licAssets = Object.entries(assets).filter(([ f ]) => f.includes(".LICENSE") && !f.includes(".debug"));
// if (licAssets.length > 0)
// {
// const l = this.hookstart();
// let license = "", licenseAsset;
// licAssets.forEach(([ file, source ]) =>
// {
// l.write(` add license file italic(${file})`, 2);
// license += "----------------------------------------------------\n";
// license += `${file}\n`;
// license += "----------------------------------------------------\n\n";
// license += `${source.source().toString()}\n\n\n`;
// licenseAsset ||= file;
// });
// if (licenseAsset)
// {
// const licFileName = "vendor.LICENSE",
// // src = new this.build.wp.sources.ConcatSource(...licAssets.map(([ _, s ]) => s));
// src = new this.build.wp.sources.RawSource(`${license.trimEnd()}\n`),
// contenthash = this.build.getContentHash(src);
// // this.compilation.renameAsset(licenseAsset, licFileName);
// // this.compilation.updateAsset(licFileName, source, { sourceFilename: licenseAsset, contenthash });
// // Object.keys(assets).filter((f) => f !== licFileName).forEach((f) => this.compilation.deleteAsset(f));
// this.compilation.emitAsset(licFileName, src, { contenthash, related: { files: Object.keys(licAssets) }});
// }
// this.hookdone();
// };
// };
/**
* @override
* @param {WebpackCompiler} _compiler
* @param {boolean} firstPass
* @returns {WebpackPluginInstance | undefined}
*/
getVendorPlugin(_compiler, firstPass)
{
if (!firstPass)
{
const build = this.build,
// rgxTest = /LICENSE/i,
outputFilename = build.isMultiTarget ? `vendor.${build.target}.LICENSE` : "vendor.LICENSE";
return /** @type {any} */(new LicenseWebpackPlugin({
outputFilename,
perChunkOutput: false,
skipChildCompilers: false,
// licenseInclusionTest: (l) => rgxTest.test(l),
modulesDirectories: build.wpc.resolve.modules,
stats: {
warnings: build.logger.level >= 2,
errors: !build.logger.isDisabled
}
}));
}
}
}
module.exports = WpwLicenseFilesPlugin.create;