builder/lib/processors/bundlers/moduleBundler.js

  1. const BundleBuilder = require("../../lbt/bundle/Builder");
  2. const LocatorResourcePool = require("../../lbt/resources/LocatorResourcePool");
  3. const EvoResource = require("@ui5/fs").Resource;
  4. const log = require("@ui5/logger").getLogger("builder:processors:bundlers:moduleBundler");
  5. /**
  6. * A ModuleBundleDefinitionSection specifies the embedding mode (either 'provided', 'raw', 'preload', 'require'
  7. * or 'bundleInfo') and lists the resources that should be in- or excluded from the section.
  8. * <p>
  9. * <b>Module bundle section modes</b><br>
  10. * <ul>
  11. * <li>
  12. * <code>provided</code>: A section of mode 'provided' defines a set of modules that should not be included in
  13. * the bundle file itself, but which should be assumed to be already loaded (or 'provided') by the environment into
  14. * which the bundle module is loaded.
  15. * </li>
  16. * <li>
  17. * <code>raw</code>: A 'raw' section determines the set of modules that should be embedded, sorts them according
  18. * to their dependencies and writes them out 1:1 without any transformation or wrapping (raw). Only JavaScript
  19. * sources can be embedded in a raw section.
  20. * </li>
  21. * <li>
  22. * <code>preload</code>: A 'preload' section packages resources that should be stored in the preload cache in the
  23. * client. They can embed any textual resource type (JavaScript, XML, JSON and .properties files) that the
  24. * bundling supports. UI5 modules are wrapped into a 'sap.ui.predefine' call. Other JavaScript modules will be
  25. * embedded into a 'jQuery.sap.registerPreload' call, or in a "sap.ui.require.preload" call when
  26. * the ui5loader is available.
  27. * </li>
  28. * <li>
  29. * <code>require</code>: A 'require' section is transformed into a sequence of jQuery.sap.require calls. The
  30. * list will be resolved like an include pattern list in any of the other sections and for each of the resolved
  31. * modules, a jQuery.sap.require will be created. In case the ui5loader is available, 'sap.ui.requireSync' is
  32. * used instead.
  33. * </li>
  34. * <li>
  35. * <code>bundleInfo</code>: A 'bundleInfo' section describes the content of another named bundle. This information
  36. * is transformed into a ui5loader-"bundlesUI5" configuration.
  37. * At runtime, if a module is known to be contained in a bundle, the loader will require that bundle before
  38. * the module itself.
  39. * This requires the ui5loader to be available at build time and UI5 version 1.74.0 or higher at runtime.
  40. * </li>
  41. * </ul>
  42. * </p>
  43. *
  44. * @public
  45. * @typedef {object} ModuleBundleDefinitionSection
  46. * @property {string} mode The embedding mode. Either 'provided', 'raw', 'preload', 'require' or 'bundleInfo'
  47. * @property {string[]} filters List of modules declared as glob patterns (resource name patterns) that should be
  48. * in- or excluded.
  49. * A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk,
  50. * denote an arbitrary number of characters or folder names.
  51. * Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later
  52. * exclusion overrides an earlier inclusion, and vice versa.
  53. * @example <caption>List of modules as glob patterns that should be in- or excluded</caption>
  54. * // Includes everything from "some/path/to/module/",
  55. * // but excludes the subfolder "some/path/to/module/to/be/excluded/"
  56. * const section = {
  57. * "filters": [
  58. * "some/path/to/module/",
  59. * "!some/path/to/module/to/be/excluded/"
  60. * ]
  61. * };
  62. *
  63. * @property {boolean} [resolve=false] Whether (transitive) dependencies of modules that match the given filters
  64. * should be resolved and added to the module set
  65. * @property {boolean} [resolveConditional=false] Whether conditional dependencies of modules should be resolved
  66. * and added to the module set for this section
  67. * @property {boolean} [renderer=false] Whether renderers for controls should be added to the module set
  68. * @property {boolean} [declareRawModules=false] Whether raw modules should be declared after jQuery.sap.global
  69. * became available. With the usage of the ui5loader, this flag should be set to 'false'
  70. * @property {boolean} [sort=true] Whether the modules should be sorted by their dependencies
  71. */
  72. /* eslint-disable max-len */
  73. /**
  74. * Module bundle definition
  75. *
  76. * @public
  77. * @typedef {object} ModuleBundleDefinition
  78. * @property {string} name The module bundle name
  79. * @property {string[]} [defaultFileTypes=[".js", ".control.xml", ".fragment.html", ".fragment.json", ".fragment.xml", ".view.html", ".view.json", ".view.xml"]]
  80. * List of default file types to be included in the bundle
  81. * @property {ModuleBundleDefinitionSection[]} sections List of module bundle definition sections.
  82. */
  83. /* eslint-enable max-len */
  84. /**
  85. * Module bundle options
  86. *
  87. * @public
  88. * @typedef {object} ModuleBundleOptions
  89. * @property {boolean} [optimize=false] If set to 'true' the module bundle gets minified
  90. * @property {boolean} [decorateBootstrapModule=false] If set to 'false', the module won't be decorated
  91. * with an optimization marker
  92. * @property {boolean} [addTryCatchRestartWrapper=false] Whether to wrap bootable module bundles with
  93. * a try/catch to filter out "Restart" errors
  94. * @property {boolean} [usePredefineCalls=false] If set to 'true', sap.ui.predefine is used for UI5 modules
  95. * @property {number} [numberOfParts=1] The number of parts the module bundle should be splitted
  96. * @property {boolean} [ignoreMissingModules=false] When searching for modules which are optional for further
  97. * processing, do not throw in case they are missing
  98. */
  99. /**
  100. * Legacy preload bundler.
  101. *
  102. * @public
  103. * @alias module:@ui5/builder.processors.moduleBundler
  104. * @param {object} parameters Parameters
  105. * @param {module:@ui5/fs.Resource[]} parameters.resources Resources
  106. * @param {object} parameters.options Options
  107. * @param {ModuleBundleDefinition} parameters.options.bundleDefinition Module bundle definition
  108. * @param {ModuleBundleOptions} parameters.options.bundleOptions Module bundle options
  109. * @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving with module bundle resources
  110. */
  111. module.exports = function({resources, options: {bundleDefinition, bundleOptions}}) {
  112. // console.log("preloadBundler bundleDefinition:");
  113. // console.log(JSON.stringify(options.bundleDefinition, null, 4));
  114. // TODO 3.0: Fix defaulting behavior, align with JSDoc
  115. bundleOptions = bundleOptions || {optimize: true};
  116. const pool = new LocatorResourcePool({
  117. ignoreMissingModules: bundleOptions.ignoreMissingModules
  118. });
  119. const builder = new BundleBuilder(pool);
  120. if (log.isLevelEnabled("verbose")) {
  121. log.verbose(`Generating bundle:`);
  122. log.verbose(`bundleDefinition: ${JSON.stringify(bundleDefinition, null, 2)}`);
  123. log.verbose(`bundleOptions: ${JSON.stringify(bundleOptions, null, 2)}`);
  124. }
  125. return pool.prepare( resources ).
  126. then( () => builder.createBundle(bundleDefinition, bundleOptions) ).
  127. then( (results) => {
  128. let bundles;
  129. if (results instanceof Array) {
  130. bundles = results;
  131. } else {
  132. bundles = [results];
  133. }
  134. return Promise.all(bundles.map((bundleObj) => {
  135. if ( bundleObj ) {
  136. const {name, content} = bundleObj;
  137. // console.log("creating bundle as '%s'", "/resources/" + name);
  138. const resource = new EvoResource({
  139. path: "/resources/" + name,
  140. string: content
  141. });
  142. return resource;
  143. }
  144. }));
  145. });
  146. };