File: D:/web/matomo.circ/plugins/CoreConsole/Commands/ComputeJsAssetSize.php
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\AssetManager;
use Piwik\Development;
use Piwik\Metrics\Formatter;
use Piwik\Plugin;
use Piwik\Plugin\ConsoleCommand;
use Piwik\Plugin\Manager;
use Piwik\ProxyHttp;
use Piwik\SettingsPiwik;
use Piwik\Theme;
class ComputeJsAssetSize extends ConsoleCommand
{
private $totals = [];
protected function configure()
{
$this->setName('development:compute-js-asset-size');
$this->setDescription('Generates production assets and computes the size of the resulting code.');
$this->addNoValueOption('no-delete', null, 'Do not delete files after creating them.');
$this->addRequiredValueOption('plugin', null, 'For submodule plugins and 3rd party plugins.');
}
public function isEnabled()
{
return SettingsPiwik::isGitDeployment();
}
protected function doExecute(): int
{
$input = $this->getInput();
$output = $this->getOutput();
$noDelete = $input->getOption('no-delete');
$plugin = $input->getOption('plugin');
$this->checkDevelopmentModeDisabled();
$this->ensureThirdPartyPluginsActivated($plugin);
$output->writeln("Building and printing sizes of built JS assets...");
$fetcher = $this->makeUmdFetcher();
$this->deleteMergedAssets();
$this->buildAssets($fetcher);
$output->writeln("");
$this->printCurrentGitHashAndBranch($plugin);
$output->writeln("");
$this->printFilesizes($fetcher);
if (!$noDelete) {
$this->deleteMergedAssets();
}
return self::SUCCESS;
}
private function ensureThirdPartyPluginsActivated($plugin = null)
{
$expectedPluginsLoadedAndActivated = [
"CorePluginsAdmin",
"CoreAdminHome",
"CoreHome",
"WebsiteMeasurable",
"IntranetMeasurable",
"Diagnostics",
"CoreVisualizations",
"Proxy",
"API",
"Widgetize",
"Transitions",
"LanguagesManager",
"Actions",
"Dashboard",
"MultiSites",
"Referrers",
"UserLanguage",
"DevicesDetection",
"Goals",
"Ecommerce",
"SEO",
"Events",
"UserCountry",
"GeoIp2",
"VisitsSummary",
"VisitFrequency",
"VisitTime",
"VisitorInterest",
"RssWidget",
"Feedback",
"Monolog",
"Login",
"TwoFactorAuth",
"UsersManager",
"SitesManager",
"Installation",
"CoreUpdater",
"CoreConsole",
"ScheduledReports",
"UserCountryMap",
"Live",
"PrivacyManager",
"ImageGraph",
"Annotations",
"MobileMessaging",
"Overlay",
"SegmentEditor",
"Insights",
"Morpheus",
"Contents",
"TestRunner",
"BulkTracking",
"Resolution",
"DevicePlugins",
"Heartbeat",
"Intl",
"UserId",
"CustomJsTracker",
"Tour",
"PagePerformance",
"CustomDimensions",
"TagManager",
"AbTesting",
"ActivityLog",
"Bandwidth",
"Cohorts",
"CustomAlerts",
"CustomReports",
"CustomVariables",
"DeviceDetectorCache",
"FormAnalytics",
"Funnels",
"GoogleAnalyticsImporter",
"InvalidateReports",
"MarketingCampaignsReporting",
"MediaAnalytics",
"MultiChannelConversionAttribution",
"QueuedTracking",
"RollUpReporting",
"SearchEngineKeywordsPerformance",
"UsersFlow",
"VisitorGenerator",
"WhiteLabel",
"WooCommerceAnalytics",
"AdvertisingConversionExport",
"AnonymousPiwikUsageMeasurement",
];
if ($plugin) {
$expectedPluginsLoadedAndActivated[] = $plugin;
}
if (is_file(PIWIK_INCLUDE_PATH . '/plugins/CoreVue/plugin.json')) {
$expectedPluginsLoadedAndActivated[] = "CoreVue";
}
$expectedPluginsLoadedAndActivated = array_unique($expectedPluginsLoadedAndActivated);
$pluginsLoadedAndActivated = Manager::getInstance()->getPluginsLoadedAndActivated();
$pluginsLoadedAndActivated = array_map(function (Plugin $p) {
return $p->getPluginName();
}, $pluginsLoadedAndActivated);
$missingPlugins = array_diff($expectedPluginsLoadedAndActivated, $pluginsLoadedAndActivated);
if (!empty($missingPlugins)) {
throw new \Exception("Activate the following plugins before running this command: " . implode(", ", $missingPlugins));
}
}
private function buildAssets(AssetManager\UIAssetFetcher\PluginUmdAssetFetcher $fetcher)
{
AssetManager::getInstance()->getMergedCoreJavaScript();
AssetManager::getInstance()->getMergedNonCoreJavaScript();
$chunks = $fetcher->getChunkFiles();
foreach ($chunks as $chunk) {
AssetManager::getInstance()->getMergedJavaScriptChunk($chunk->getChunkName());
}
}
private function deleteMergedAssets()
{
AssetManager::getInstance()->removeMergedAssets();
}
private function printFilesizes(AssetManager\UIAssetFetcher\PluginUmdAssetFetcher $fetcher)
{
$fileSizes = [];
$mergedCore = AssetManager::getInstance()->getMergedCoreJavaScript();
$fileSizes[] = $this->getFileSizeRow($mergedCore);
$mergedNonCore = AssetManager::getInstance()->getMergedNonCoreJavaScript();
$fileSizes[] = $this->getFileSizeRow($mergedNonCore);
$chunks = $fetcher->getChunkFiles();
foreach ($chunks as $chunk) {
$chunkAsset = AssetManager::getInstance()->getMergedJavaScriptChunk($chunk->getChunkName());
$fileSizes[] = $this->getFileSizeRow($chunkAsset);
}
$fileSizes[] = [];
$fileSizes[] = ['Total', $this->getFormattedSize($this->totals['merged']), $this->getFormattedSize($this->totals['gzip'])];
$this->renderTable(['File', 'Size', 'Size (gzipped)'], $fileSizes);
}
private function getFileSize($fileLocation, $type)
{
$size = filesize($fileLocation);
$this->totals[$type] = ($this->totals[$type] ?? 0) + $size;
return $this->getFormattedSize($size);
}
private function getFormattedSize($size)
{
$formatter = new Formatter();
$size = $formatter->getPrettySizeFromBytes($size, 'K', 2);
return $size;
}
private function checkDevelopmentModeDisabled()
{
if (Development::isEnabled()) {
throw new \Exception("This command is to estimate production build sizes, so development mode must be disabled for it.");
}
}
private function getGzippedFileSize($path)
{
$data = file_get_contents($path);
$data = ProxyHttp::gzencode($data);
if (false === $data) {
throw new \Exception('compressing file ' . $path . ' failed');
}
$compressedPath = dirname($path) . '/' . basename($path) . '.gz';
file_put_contents($compressedPath, $data);
return $this->getFileSize($compressedPath, 'gzip');
}
private function printCurrentGitHashAndBranch($plugin = null)
{
$branchName = trim(`git rev-parse --abbrev-ref HEAD`);
$lastCommit = trim(`git log --pretty=format:'%h' -n 1`);
$pluginSuffix = '';
if ($plugin) {
$prefix = 'cd "' . addslashes(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin) . '"; ';
$pluginBranchName = trim(`$prefix git rev-parse --abbrev-ref HEAD`);
$pluginLastCommit = trim(`$prefix git log --pretty=format:'%h' -n 1`);
$pluginSuffix = " [$plugin: $pluginBranchName ($pluginLastCommit)]";
}
$this->getOutput()->writeln("<info>$branchName ($lastCommit)$pluginSuffix</info>");
}
private function makeUmdFetcher()
{
$plugins = Manager::getInstance()->getPluginsLoadedAndActivated();
$pluginNames = array_map(function ($p) {
return $p->getPluginName();
}, $plugins);
$theme = Manager::getInstance()->getThemeEnabled();
if (!empty($theme)) {
$theme = new Theme();
}
$fetcher = new AssetManager\UIAssetFetcher\PluginUmdAssetFetcher($pluginNames, $theme, null);
return $fetcher;
}
private function getFileSizeRow(AssetManager\UIAsset $asset)
{
return [$asset->getRelativeLocation(), $this->getFileSize($asset->getAbsoluteLocation(), 'merged'), $this->getGzippedFileSize($asset->getAbsoluteLocation())];
}
}