builder/lib/processors/bundlers/moduleBundler.js

  1. import BundleBuilder from "../../lbt/bundle/Builder.js";
  2. import LocatorResourcePool from "../../lbt/resources/LocatorResourcePool.js";
  3. import EvoResource from "@ui5/fs/Resource";
  4. import {getLogger} from "@ui5/logger";
  5. const log = getLogger("builder:processors:bundlers:moduleBundler");
  6. /**
  7. * @public
  8. * @module @ui5/builder/processors/bundlers/moduleBundler
  9. */
  10. /**
  11. * A ModuleBundleDefinitionSection specifies the embedding mode (either 'provided', 'raw', 'preload', 'require'
  12. * or 'bundleInfo') and lists the resources that should be in- or excluded from the section.
  13. * <p>
  14. * <b>Module bundle section modes</b><br>
  15. * <ul>
  16. * <li>
  17. * <code>provided</code>: A section of mode 'provided' defines a set of modules that should not be included in
  18. * the bundle file itself, but which should be assumed to be already loaded (or 'provided') by the environment into
  19. * which the bundle module is loaded.
  20. * </li>
  21. * <li>
  22. * <code>raw</code>: A 'raw' section determines the set of modules that should be embedded, sorts them according
  23. * to their dependencies and writes them out 1:1 without any transformation or wrapping (raw). Only JavaScript
  24. * sources can be embedded in a raw section.
  25. * </li>
  26. * <li>
  27. * <code>preload</code>: A 'preload' section packages resources that should be stored in the preload cache in the
  28. * client. They can embed any textual resource type (JavaScript, XML, JSON and .properties files) that the
  29. * bundling supports. UI5 modules are wrapped into a 'sap.ui.predefine' call. Other JavaScript modules will be
  30. * embedded into a 'jQuery.sap.registerPreload' call, or in a "sap.ui.require.preload" call when
  31. * the ui5loader is available.
  32. * </li>
  33. * <li>
  34. * <code>require</code>: A `require` section is transformed into a `sap.ui.require` call with all the dependencies
  35. * resolved. This module comes with an `async` flag. When set to false, the modules
  36. * are loaded using `sap.ui.requireSync` instead of `sap.ui.require`.
  37. * **Note:** The `sap.ui.requireSync` API is not available in UI5 version 2.x.
  38. * </li>
  39. * <li>
  40. * <code>bundleInfo</code>: A 'bundleInfo' section describes the content of another named bundle. This information
  41. * is transformed into a ui5loader-"bundlesUI5" configuration.
  42. * At runtime, if a module is known to be contained in a bundle, the loader will require that bundle before
  43. * the module itself.
  44. * This requires the ui5loader to be available at build time and UI5 version 1.74.0 or higher at runtime.
  45. * </li>
  46. * </ul>
  47. * </p>
  48. *
  49. * @public
  50. * @typedef {object} ModuleBundleDefinitionSection
  51. * @property {string} mode The embedding mode. Either 'provided', 'raw', 'preload', 'require' or 'bundleInfo'
  52. * @property {string[]} filters List of modules declared as glob patterns (resource name patterns) that should be
  53. * in- or excluded.
  54. * A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk,
  55. * denote an arbitrary number of characters or folder names.
  56. * Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later
  57. * exclusion overrides an earlier inclusion, and vice versa.
  58. * @example <caption>List of modules as glob patterns that should be in- or excluded</caption>
  59. * // Includes everything from "some/path/to/module/",
  60. * // but excludes the subfolder "some/path/to/module/to/be/excluded/"
  61. * const section = {
  62. * "filters": [
  63. * "some/path/to/module/",
  64. * "!some/path/to/module/to/be/excluded/"
  65. * ]
  66. * };
  67. *
  68. * @property {boolean} [resolve=false] Whether (transitive) dependencies of modules that match the given filters
  69. * should be resolved and added to the module set
  70. * @property {boolean} [resolveConditional=false] Whether conditional dependencies of modules should be resolved
  71. * and added to the module set for this section
  72. * @property {boolean} [renderer=false] Whether renderers for controls should be added to the module set
  73. * @property {boolean} [declareRawModules=false] Whether raw modules should be declared after jQuery.sap.global
  74. * became available. With the usage of the ui5loader, this flag should be set to 'false'
  75. * @property {boolean} [sort=true] Whether the modules should be sorted by their dependencies
  76. * @property {boolean} [async=true] Whether the `require` section of the module should be loaded asynchronously.
  77. * When set to true, the modules are loaded using a single `sap.ui.require` call instead of multiple
  78. * `sap.ui.requireSync` calls.
  79. * The latter API is not available in UI5 version 2.x.
  80. * **Note:** This property is available only for `mode=require`.
  81. */
  82. /* eslint-disable max-len */
  83. /**
  84. * Module bundle definition
  85. *
  86. * @public
  87. * @typedef {object} ModuleBundleDefinition
  88. * @property {string} name The module bundle name
  89. * @property {string[]} [defaultFileTypes=[".js", ".control.xml", ".fragment.html", ".fragment.json", ".fragment.xml", ".view.html", ".view.json", ".view.xml"]]
  90. * List of default file types to be included in the bundle
  91. * @property {module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundleDefinitionSection[]} sections List of module bundle definition sections.
  92. */
  93. /* eslint-enable max-len */
  94. /**
  95. * Module bundle options
  96. *
  97. * @public
  98. * @typedef {object} ModuleBundleOptions
  99. * @property {boolean} [optimize=true] Whether the module bundle gets minified
  100. * @property {boolean} [sourceMap=true] Whether to generate a source map file for the bundle
  101. * @property {boolean} [decorateBootstrapModule=false] If set to 'false', bootable bundles won't be decorated
  102. * with an optimization marker
  103. * @property {boolean} [addTryCatchRestartWrapper=false] Whether to wrap bootable bundles with
  104. * a try/catch to filter out "Restart" errors
  105. * @property {number} [numberOfParts=1] The number of parts the module bundle should be splitted
  106. * @property {boolean} [ignoreMissingModules=false] When searching for modules which are optional for further
  107. * processing, do not throw in case they are missing
  108. */
  109. /**
  110. * Result set
  111. *
  112. * @public
  113. * @typedef {object} ModuleBundlerResult
  114. * @property {@ui5/fs/Resource} bundle Bundle resource
  115. * @property {@ui5/fs/Resource} sourceMap Source Map
  116. */
  117. /* eslint-disable max-len */
  118. /**
  119. * Legacy module bundler.
  120. *
  121. * @public
  122. * @function default
  123. * @static
  124. *
  125. * @param {object} parameters Parameters
  126. * @param {@ui5/fs/Resource[]} parameters.resources Resources
  127. * @param {object} parameters.options Options
  128. * @param {object} [parameters.options.moduleNameMapping]
  129. Optional mapping of resource paths to module name in order to overwrite the default determination
  130. * @param {module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundleDefinition} parameters.options.bundleDefinition Module
  131. bundle definition
  132. * @param {module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundleOptions} [parameters.options.bundleOptions] Module
  133. bundle options
  134. * @param {string} [parameters.options.targetUi5CoreVersion] Optional semver compliant sap.ui.core project version, e.g '2.0.0'.
  135. This allows the bundler to make assumptions on available runtime APIs.
  136. Omit if the ultimate UI5 version at runtime is unknown or can't be determined.
  137. * @param {boolean} [parameters.options.allowStringBundling=false] Optional flag to allow bundling of modules as a string.
  138. * @returns {Promise<module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundlerResult[]>}
  139. * Promise resolving with module bundle resources
  140. */
  141. /* eslint-enable max-len */
  142. export default function({resources, options: {
  143. bundleDefinition, bundleOptions, moduleNameMapping, targetUi5CoreVersion, allowStringBundling = false
  144. }}) {
  145. // Apply defaults without modifying the passed object
  146. bundleOptions = Object.assign({}, {
  147. optimize: true,
  148. sourceMap: true,
  149. decorateBootstrapModule: false,
  150. addTryCatchRestartWrapper: false,
  151. numberOfParts: 1,
  152. ignoreMissingModules: false
  153. }, bundleOptions);
  154. // bundleDefinition's defaults get applied in the corresponding standard tasks
  155. const pool = new LocatorResourcePool({
  156. ignoreMissingModules: bundleOptions.ignoreMissingModules
  157. });
  158. const builder = new BundleBuilder(pool, targetUi5CoreVersion, allowStringBundling);
  159. if (log.isLevelEnabled("verbose")) {
  160. log.verbose(`Generating bundle:`);
  161. log.verbose(`bundleDefinition: ${JSON.stringify(bundleDefinition, null, 2)}`);
  162. log.verbose(`bundleOptions: ${JSON.stringify(bundleOptions, null, 2)}`);
  163. }
  164. return pool.prepare( resources, moduleNameMapping ).
  165. then( () => builder.createBundle(bundleDefinition, bundleOptions) ).
  166. then( (results) => {
  167. let bundles;
  168. if (results instanceof Array) {
  169. bundles = results;
  170. } else {
  171. bundles = [results];
  172. }
  173. return Promise.all(bundles.map((bundleObj) => {
  174. if ( bundleObj ) {
  175. const {name, content, sourceMap} = bundleObj;
  176. // console.log("creating bundle as '%s'", "/resources/" + name);
  177. const res = {};
  178. res.bundle = new EvoResource({
  179. path: "/resources/" + name,
  180. string: content
  181. });
  182. if (sourceMap) {
  183. res.sourceMap = new EvoResource({
  184. path: "/resources/" + name + ".map",
  185. string: sourceMap
  186. });
  187. }
  188. return res;
  189. }
  190. }));
  191. });
  192. }