RequireJs.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Deploy\Package\Bundle;
  7. use Magento\Deploy\Config\BundleConfig;
  8. use Magento\Deploy\Package\BundleInterface;
  9. use Magento\Framework\Filesystem;
  10. use \Magento\Framework\Filesystem\File\WriteInterface;
  11. use Magento\Framework\App\Filesystem\DirectoryList;
  12. use Magento\Framework\View\Asset\Minification;
  13. /**
  14. * RequireJs static files bundle object
  15. *
  16. * All files added will be bundled to multiple bundle files compatible with RequireJS AMD format
  17. */
  18. class RequireJs implements BundleInterface
  19. {
  20. /**
  21. * Static files Bundling configuration class
  22. *
  23. * @var BundleConfig
  24. */
  25. private $bundleConfig;
  26. /**
  27. * Helper class for static files minification related processes
  28. *
  29. * @var Minification
  30. */
  31. private $minification;
  32. /**
  33. * Static content directory writable interface
  34. *
  35. * @var WriteInterface
  36. */
  37. private $staticDir;
  38. /**
  39. * Package area
  40. *
  41. * @var string
  42. */
  43. private $area;
  44. /**
  45. * Package theme
  46. *
  47. * @var string
  48. */
  49. private $theme;
  50. /**
  51. * Package locale
  52. *
  53. * @var string
  54. */
  55. private $locale;
  56. /**
  57. * Bundle content pools
  58. *
  59. * @var string[]
  60. */
  61. private $contentPools = [
  62. 'js' => 'jsbuild',
  63. 'html' => 'text'
  64. ];
  65. /**
  66. * Files to be bundled
  67. *
  68. * @var array[]
  69. */
  70. private $files = [
  71. 'jsbuild' => [],
  72. 'text' => []
  73. ];
  74. /**
  75. * Files content cache
  76. *
  77. * @var string[]
  78. */
  79. private $fileContent = [];
  80. /**
  81. * Incremental index of bundle file
  82. *
  83. * Chosen bundling strategy may result in creating multiple bundle files instead of one
  84. *
  85. * @var int
  86. */
  87. private $bundleFileIndex = 0;
  88. /**
  89. * Relative path to directory where bundle files should be created
  90. *
  91. * @var string
  92. */
  93. private $pathToBundleDir;
  94. /**
  95. * Bundle constructor
  96. *
  97. * @param Filesystem $filesystem
  98. * @param BundleConfig $bundleConfig
  99. * @param Minification $minification
  100. * @param string $area
  101. * @param string $theme
  102. * @param string $locale
  103. * @param array $contentPools
  104. */
  105. public function __construct(
  106. Filesystem $filesystem,
  107. BundleConfig $bundleConfig,
  108. Minification $minification,
  109. $area,
  110. $theme,
  111. $locale,
  112. array $contentPools = []
  113. ) {
  114. $this->filesystem = $filesystem;
  115. $this->bundleConfig = $bundleConfig;
  116. $this->minification = $minification;
  117. $this->staticDir = $filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
  118. $this->area = $area;
  119. $this->theme = $theme;
  120. $this->locale = $locale;
  121. $this->contentPools = array_merge($this->contentPools, $contentPools);
  122. $this->pathToBundleDir = $this->area . '/' . $this->theme . '/' . $this->locale . '/' . self::BUNDLE_JS_DIR;
  123. }
  124. /**
  125. * @inheritdoc
  126. */
  127. public function addFile($filePath, $sourcePath, $contentType)
  128. {
  129. // all unknown content types designated to "text" pool
  130. $contentPoolName = isset($this->contentPools[$contentType]) ? $this->contentPools[$contentType] : 'text';
  131. $this->files[$contentPoolName][$filePath] = $sourcePath;
  132. return true;
  133. }
  134. /**
  135. * @inheritdoc
  136. */
  137. public function flush()
  138. {
  139. $this->bundleFileIndex = 0;
  140. $bundleFile = null;
  141. foreach ($this->files as $contentPoolName => $files) {
  142. if (empty($files)) {
  143. continue;
  144. }
  145. $content = [];
  146. $freeSpace = $this->getBundleFileMaxSize();
  147. $bundleFile = $this->startNewBundleFile($contentPoolName);
  148. foreach ($files as $filePath => $sourcePath) {
  149. $fileContent = $this->getFileContent($sourcePath);
  150. $size = mb_strlen($fileContent, 'utf-8') / 1024;
  151. if ($freeSpace > $size) {
  152. $freeSpace -= $size;
  153. $content[$this->minification->addMinifiedSign($filePath)] = $fileContent;
  154. } else {
  155. $this->endBundleFile($bundleFile, $content);
  156. $freeSpace = $this->getBundleFileMaxSize();
  157. $freeSpace -= $size;
  158. $content = [
  159. $this->minification->addMinifiedSign($filePath) => $fileContent
  160. ];
  161. $bundleFile = $this->startNewBundleFile($contentPoolName);
  162. }
  163. }
  164. $this->endBundleFile($bundleFile, $content);
  165. }
  166. if ($bundleFile) {
  167. $bundleFile->write($this->getInitJs());
  168. }
  169. $this->files = [];
  170. }
  171. /**
  172. * @inheritdoc
  173. */
  174. public function clear()
  175. {
  176. $this->staticDir->delete($this->pathToBundleDir);
  177. }
  178. /**
  179. * Create new bundle file and write beginning content to it
  180. *
  181. * @param string $contentPoolName
  182. * @return WriteInterface
  183. */
  184. private function startNewBundleFile($contentPoolName)
  185. {
  186. $bundleFile = $this->staticDir->openFile(
  187. $this->minification->addMinifiedSign($this->pathToBundleDir . '/bundle' . $this->bundleFileIndex . '.js')
  188. );
  189. $bundleFile->write("require.config({\"config\": {\n");
  190. $bundleFile->write(" \"{$contentPoolName}\":");
  191. ++$this->bundleFileIndex;
  192. return $bundleFile;
  193. }
  194. /**
  195. * Write ending content to bundle file
  196. *
  197. * @param WriteInterface $bundleFile
  198. * @param array $contents
  199. * @return bool true on success
  200. */
  201. private function endBundleFile(WriteInterface $bundleFile, array $contents)
  202. {
  203. if ($contents) {
  204. $content = json_encode($contents, JSON_UNESCAPED_SLASHES);
  205. $bundleFile->write("{$content}\n");
  206. } else {
  207. $bundleFile->write("{}\n");
  208. }
  209. $bundleFile->write("}});\n");
  210. return true;
  211. }
  212. /**
  213. * Get content of static file
  214. *
  215. * @param string $sourcePath
  216. * @return string
  217. */
  218. private function getFileContent($sourcePath)
  219. {
  220. if (!isset($this->fileContent[$sourcePath])) {
  221. $content = $this->staticDir->readFile($this->minification->addMinifiedSign($sourcePath));
  222. if (mb_detect_encoding($content) !== "UTF-8") {
  223. $content = mb_convert_encoding($content, "UTF-8");
  224. }
  225. $this->fileContent[$sourcePath] = $content;
  226. }
  227. return $this->fileContent[$sourcePath];
  228. }
  229. /**
  230. * Get max size of bundle files (in KB)
  231. *
  232. * @return int
  233. */
  234. private function getBundleFileMaxSize()
  235. {
  236. return $this->bundleConfig->getBundleFileMaxSize($this->area, $this->theme);
  237. }
  238. /**
  239. * Bundle initialization script content (this must be added to the latest bundle file at the very end)
  240. *
  241. * @return string
  242. */
  243. private function getInitJs()
  244. {
  245. return "require.config({\n" .
  246. " bundles: {\n" .
  247. " 'mage/requirejs/static': [\n" .
  248. " 'jsbuild',\n" .
  249. " 'buildTools',\n" .
  250. " 'text',\n" .
  251. " 'statistician'\n" .
  252. " ]\n" .
  253. " },\n" .
  254. " deps: [\n" .
  255. " 'jsbuild'\n" .
  256. " ]\n" .
  257. "});\n";
  258. }
  259. }