- import {getLogger} from "@ui5/logger";
-const log = getLogger("builder:tasks:bundlers:generateLibraryPreload");
-import moduleBundler from "../../processors/bundlers/moduleBundler.js";
-import {negateFilters} from "../../lbt/resources/ResourceFilterList.js";
-import createModuleNameMapping from "./utils/createModuleNameMapping.js";
-
-function getDefaultLibraryPreloadFilters(namespace, excludes) {
- const filters = [
- `${namespace}/`,
- `${namespace}/**/manifest.json`,
- `!${namespace}/**/*-preload.js`, // exclude all bundles
- `!${namespace}/designtime/`,
- `!${namespace}/**/*.designtime.js`,
- `!${namespace}/**/*.support.js`
- ];
-
- if (Array.isArray(excludes)) {
- const allFilterExcludes = negateFilters(excludes);
- // Add configured excludes at the end of filter list
- allFilterExcludes.forEach((filterExclude) => {
- // Allow all excludes (!) and limit re-includes (+) to the library namespace
- if (filterExclude.startsWith("!") || filterExclude.startsWith(`+${namespace}/`)) {
- filters.push(filterExclude);
- } else {
- log.warn(`Configured preload exclude contains invalid re-include: !${filterExclude.substr(1)}. ` +
- `Re-includes must start with the library's namespace ${namespace}`);
- }
- });
- }
-
- return filters;
-}
-
-function getBundleDefinition(namespace, excludes) {
- // TODO: move to config of actual core project
- if (namespace === "sap/ui/core") {
- return {
- name: `${namespace}/library-preload.js`,
- sections: [
- {
- // exclude the content of sap-ui-core by declaring it as 'provided'
- mode: "provided",
- filters: [
- "ui5loader-autoconfig.js",
- "sap/ui/core/Core.js"
- ],
- resolve: true
- },
- {
- mode: "preload",
- filters: [
- // Note: Don't pass configured preload excludes for sap.ui.core
- // as they are already hardcoded below.
- // In future the sap/ui/core/library-preload should be configured
- // as a custom bundle in the ui5.yaml.
- ...getDefaultLibraryPreloadFilters(namespace),
-
- `!${namespace}/cldr/`,
- "*.js",
- "sap/base/",
- "sap/ui/base/",
- "sap/ui/dom/",
- "sap/ui/events/",
- "sap/ui/model/",
- "sap/ui/security/",
- "sap/ui/util/",
- "sap/ui/Global.js",
-
- // include only thirdparty that is very likely to be used
- "sap/ui/thirdparty/crossroads.js",
- "sap/ui/thirdparty/caja-html-sanitizer.js",
- "sap/ui/thirdparty/hasher.js",
- "sap/ui/thirdparty/signals.js",
- "sap/ui/thirdparty/jquery-mobile-custom.js",
- "sap/ui/thirdparty/jqueryui/jquery-ui-core.js",
- "sap/ui/thirdparty/jqueryui/jquery-ui-position.js",
-
- // other excludes (not required for productive scenarios)
- "!sap-ui-*.js",
- "!sap/ui/core/support/",
- "!sap/ui/core/plugin/DeclarativeSupport.js",
- "!sap/ui/core/plugin/LessSupport.js"
-
- ],
- resolve: false,
- resolveConditional: false,
- renderer: true
- }
- ]
- };
- }
- return {
- name: `${namespace}/library-preload.js`,
- sections: [
- {
- mode: "preload",
- filters: getDefaultLibraryPreloadFilters(namespace, excludes),
- resolve: false,
- resolveConditional: false,
- renderer: true
- }
- ]
- };
-}
-
-function getDesigntimeBundleDefinition(namespace) {
- return {
- name: `${namespace}/designtime/library-preload.designtime.js`,
- sections: [
- {
- mode: "preload",
- filters: [
- `${namespace}/**/*.designtime.js`,
- `${namespace}/designtime/`,
- `!${namespace}/**/*-preload.designtime.js`,
- `!${namespace}/designtime/**/*.properties`,
- `!${namespace}/designtime/**/*.svg`,
- `!${namespace}/designtime/**/*.xml`
- ],
- resolve: false,
- resolveConditional: false,
- renderer: false
- }
- ]
- };
-}
-
-function getSupportFilesBundleDefinition(namespace) {
- return {
- name: `${namespace}/library-preload.support.js`,
- sections: [
- {
- mode: "preload",
- filters: [
- `${namespace}/**/*.support.js`,
- `!${namespace}/**/*-preload.support.js`
- ],
- resolve: false,
- resolveConditional: false,
- renderer: false
- }
- ]
- };
-}
-
-function getModuleBundlerOptions(config) {
- const moduleBundlerOptions = {};
-
- // required in sap-ui-core-nojQuery.js and sap-ui-core-nojQuery-dbg.js
- const providedSection = {
- mode: "provided",
- filters: [
- "jquery-ui-core.js",
- "jquery-ui-datepicker.js",
- "jquery-ui-position.js",
- "sap/ui/thirdparty/jquery.js",
- "sap/ui/thirdparty/jquery/*",
- "sap/ui/thirdparty/jqueryui/*"
- ]
- };
-
- moduleBundlerOptions.bundleOptions = {
- optimize: config.preload,
- decorateBootstrapModule: config.preload,
- addTryCatchRestartWrapper: config.preload,
- usePredefineCalls: config.preload
- };
-
- moduleBundlerOptions.bundleDefinition = getSapUiCoreBunDef(config.name, config.filters, config.preload);
-
- if (config.provided) {
- moduleBundlerOptions.bundleDefinition.sections.unshift(providedSection);
- }
-
- if (config.moduleNameMapping) {
- moduleBundlerOptions.moduleNameMapping = config.moduleNameMapping;
- }
-
- return moduleBundlerOptions;
-}
-
-function getSapUiCoreBunDef(name, filters, preload) {
- const bundleDefinition = {
- name,
- sections: []
- };
-
- // add raw section
- bundleDefinition.sections.push({
- // include all 'raw' modules that are needed for the UI5 loader
- mode: "raw",
- filters,
- resolve: true, // dependencies for raw modules are taken from shims in .library files
- sort: true, // topological sort on raw modules is mandatory
- declareModules: false
- });
-
- if (preload) {
- // add preload section
- bundleDefinition.sections.push({
- mode: "preload",
- filters: [
- "sap/ui/core/Core.js"
- ],
- resolve: true
- });
- }
-
- // add require section
- bundleDefinition.sections.push({
- mode: "require",
- filters: [
- "sap/ui/core/Core.js"
- ]
- });
-
- return bundleDefinition;
-}
-
-/**
- * @public
- * @module @ui5/builder/tasks/bundlers/generateLibraryPreload
- */
-
-/**
- * Task for library bundling.
- *
- * @public
- * @function default
- * @static
- *
- * @param {object} parameters Parameters
- * @param {@ui5/fs/DuplexCollection} parameters.workspace DuplexCollection to read and write files
- * @param {@ui5/project/build/helpers/TaskUtil} [parameters.taskUtil] TaskUtil
- * @param {object} parameters.options Options
- * @param {string} parameters.options.projectName Project name
- * @param {string[]} [parameters.options.skipBundles] Names of bundles that should not be created
- * @param {string[]} [parameters.options.excludes=[]] List of modules declared as glob patterns (resource name patterns)
- * that should be excluded from the library-preload.js bundle.
- * A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk,
- * denote an arbitrary number of characters or folder names.
- * Re-includes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later
- * inclusion overrides an earlier exclusion, and vice versa.
- * @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
- */
-export default async function({workspace, taskUtil, options: {skipBundles = [], excludes = [], projectName}}) {
- let nonDbgWorkspace = workspace;
- if (taskUtil) {
- nonDbgWorkspace = taskUtil.resourceFactory.createFilterReader({
- reader: workspace,
- callback: function(resource) {
- // Remove any debug variants
- return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
- }
- });
- }
-
- const execModuleBundlerIfNeeded = ({options, resources}) => {
- if (skipBundles.includes(options.bundleDefinition.name)) {
- log.verbose(`Skipping generation of bundle ${options.bundleDefinition.name}`);
- return null;
- }
- return moduleBundler({options, resources});
- };
-
- return nonDbgWorkspace.byGlob("/**/*.{js,json,xml,html,properties,library,js.map}").then(async (resources) => {
- // Find all libraries and create a library-preload.js bundle
-
- let p = Promise.resolve();
-
- // Create core bundles for older versions (<1.97.0) which don't define bundle configuration in the ui5.yaml
- // See: https://github.com/SAP/openui5/commit/ff127fd2d009162ea43ad312dec99d759ebc23a0
- if (projectName === "sap.ui.core") {
- // Instead of checking the sap.ui.core library version, the specVersion is checked against all versions
- // that have been defined for sap.ui.core before the bundle configuration has been introduced.
- // This is mainly to have an easier check without version parsing or using semver.
- // If no project/specVersion is available, the bundles should also be created to not break potential
- // existing use cases without a properly formed/formatted project tree.
- if (!taskUtil || taskUtil.getProject().getSpecVersion().lte("2.0")) {
- const isEvo = resources.find((resource) => {
- return resource.getPath() === "/resources/ui5loader.js";
- });
-
- let unoptimizedModuleNameMapping;
- let unoptimizedResources = resources;
- if (taskUtil) {
- const unoptimizedWorkspace = taskUtil.resourceFactory.createFilterReader({
- reader: workspace,
- callback: function(resource) {
- // Remove any non-debug variants
- return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
- }
- });
- unoptimizedResources =
- await unoptimizedWorkspace.byGlob("/**/*.{js,json,xml,html,properties,library,js.map}");
-
- unoptimizedModuleNameMapping = createModuleNameMapping({
- resources: unoptimizedResources,
- taskUtil
- });
- }
-
- let filters;
- if (isEvo) {
- filters = ["ui5loader-autoconfig.js"];
- } else {
- filters = ["jquery.sap.global.js"];
- }
- p = Promise.all([
- execModuleBundlerIfNeeded({
- options: getModuleBundlerOptions({name: "sap-ui-core.js", filters, preload: true}),
- resources
- }),
- execModuleBundlerIfNeeded({
- options: getModuleBundlerOptions({
- name: "sap-ui-core-dbg.js", filters, preload: false,
- moduleNameMapping: unoptimizedModuleNameMapping
- }),
- resources: unoptimizedResources
- }),
- execModuleBundlerIfNeeded({
- options: getModuleBundlerOptions({
- name: "sap-ui-core-nojQuery.js", filters, preload: true, provided: true
- }),
- resources
- }),
- execModuleBundlerIfNeeded({
- options: getModuleBundlerOptions({
- name: "sap-ui-core-nojQuery-dbg.js", filters, preload: false, provided: true,
- moduleNameMapping: unoptimizedModuleNameMapping
- }),
- resources: unoptimizedResources
- }),
- ]).then((results) => {
- const bundles = Array.prototype.concat.apply([], results).filter(Boolean);
- return Promise.all(bundles.map(({bundle, sourceMap}) => {
- if (taskUtil) {
- taskUtil.setTag(bundle, taskUtil.STANDARD_TAGS.IsBundle);
- if (sourceMap) {
- // Clear tag that might have been set by the minify task, in cases where
- // the bundle name is identical to a source file
- taskUtil.clearTag(sourceMap, taskUtil.STANDARD_TAGS.OmitFromBuildResult);
- }
- }
- const writes = [workspace.write(bundle)];
- if (sourceMap) {
- writes.push(workspace.write(sourceMap));
- }
- return Promise.all(writes);
- }));
- });
- }
- }
-
- return p.then(() => {
- return workspace.byGlob("/resources/**/.library").then((libraryIndicatorResources) => {
- if (libraryIndicatorResources.length > 0) {
- return libraryIndicatorResources;
- } else {
- // Fallback to "library.js" as library indicator
- log.verbose(
- `Could not find a ".library" file for project ${projectName}, ` +
- `falling back to "library.js".`);
- return workspace.byGlob("/resources/**/library.js");
- }
- }).then((libraryIndicatorResources) => {
- if (libraryIndicatorResources.length < 1) {
- // No library found - nothing to do
- log.verbose(
- `Could not find a ".library" or "library.js" file for project ${projectName}. ` +
- `Skipping library preload bundling.`);
- return;
- }
-
- return Promise.all(libraryIndicatorResources.map(async (libraryIndicatorResource) => {
- // Determine library namespace from library indicator file path
- // ending with either ".library" or "library.js" (see fallback logic above)
- // e.g. /resources/sap/foo/.library => sap/foo
- // /resources/sap/bar/library.js => sap/bar
- const libraryNamespacePattern = /^\/resources\/(.*)\/(?:\.library|library\.js)$/;
- const libraryIndicatorPath = libraryIndicatorResource.getPath();
- const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern);
- if (libraryNamespaceMatch && libraryNamespaceMatch[1]) {
- const libraryNamespace = libraryNamespaceMatch[1];
- const results = await Promise.all([
- execModuleBundlerIfNeeded({
- options: {
- bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
- bundleOptions: {
- optimize: true,
- usePredefineCalls: true,
- ignoreMissingModules: true
- }
- },
- resources
- }),
- execModuleBundlerIfNeeded({
- options: {
- bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
- bundleOptions: {
- optimize: true,
- usePredefineCalls: true,
- ignoreMissingModules: true,
- skipIfEmpty: true
- }
- },
- resources
- }),
- execModuleBundlerIfNeeded({
- options: {
- bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
- bundleOptions: {
- optimize: false,
- usePredefineCalls: true,
- ignoreMissingModules: true,
- skipIfEmpty: true
- }
- // Note: Although the bundle uses optimize=false, there is
- // no moduleNameMapping needed, as support files are excluded from minification.
- },
- resources
- })
- ]);
- const bundles = Array.prototype.concat.apply([], results).filter(Boolean);
- return Promise.all(bundles.map(({bundle, sourceMap} = {}) => {
- if (bundle) {
- if (taskUtil) {
- taskUtil.setTag(bundle, taskUtil.STANDARD_TAGS.IsBundle);
- if (sourceMap) {
- // Clear tag that might have been set by the minify task, in cases where
- // the bundle name is identical to a source file
- taskUtil.clearTag(sourceMap,
- taskUtil.STANDARD_TAGS.OmitFromBuildResult);
- }
- }
- const writes = [workspace.write(bundle)];
- if (sourceMap) {
- writes.push(workspace.write(sourceMap));
- }
- return Promise.all(writes);
- }
- }));
- } else {
- log.verbose(
- `Could not determine library namespace from file "${libraryIndicatorPath}" ` +
- `for project ${projectName}. Skipping library preload bundling.`);
- return Promise.resolve();
- }
- }));
- });
- });
- });
-}
-
-
-