diff --git a/index.js b/index.js
index 14341491d..2dc71581f 100644
--- a/index.js
+++ b/index.js
@@ -3,10 +3,6 @@
* @public
*/
module.exports = {
- /**
- * @type {import('./lib/builder/builder')}
- */
- builder: "./lib/builder/builder",
/**
* @public
* @alias module:@ui5/builder.processors
@@ -160,41 +156,6 @@ module.exports = {
* @type {import('./lib/tasks/TaskUtil')}
*/
TaskUtil: "./lib/tasks/TaskUtil"
- },
- /**
- * @private
- * @alias module:@ui5/builder.types
- * @namespace
- */
- types: {
- /**
- * @type {typeof import('./lib/types/AbstractBuilder')}
- */
- AbstractBuilder: "./lib/types/AbstractBuilder",
- /**
- * @type {typeof import('./lib/types/AbstractFormatter')}
- */
- AbstractFormatter: "./lib/types/AbstractFormatter",
- /**
- * @type {import('./lib/types/application/applicationType')}
- */
- application: "./lib/types/application/applicationType",
- /**
- * @type {import('./lib/types/library/libraryType')}
- */
- library: "./lib/types/library/libraryType",
- /**
- * @type {import('./lib/types/themeLibrary/themeLibraryType')}
- */
- themeLibrary: "./lib/types/themeLibrary/themeLibraryType",
- /**
- * @type {import('./lib/types/module/moduleType')}
- */
- module: "./lib/types/module/moduleType",
- /**
- * @type {import('./lib/types/typeRepository')}
- */
- typeRepository: "./lib/types/typeRepository"
}
};
diff --git a/lib/builder/BuildContext.js b/lib/builder/BuildContext.js
deleted file mode 100644
index 6ed64a0ab..000000000
--- a/lib/builder/BuildContext.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const ResourceTagCollection = require("@ui5/fs").ResourceTagCollection;
-const ProjectBuildContext = require("./ProjectBuildContext");
-
-// Note: When adding standard tags, always update the public documentation in TaskUtil
-// (Type "module:@ui5/builder.tasks.TaskUtil~StandardBuildTags")
-const GLOBAL_TAGS = Object.freeze({
- IsDebugVariant: "ui5:IsDebugVariant",
- HasDebugVariant: "ui5:HasDebugVariant",
-});
-
-/**
- * Context of a build process
- *
- * @private
- * @memberof module:@ui5/builder.builder
- */
-class BuildContext {
- constructor({rootProject, options = {}}) {
- if (!rootProject) {
- throw new Error(`Missing parameter 'rootProject'`);
- }
- this.rootProject = rootProject;
- this.projectBuildContexts = [];
- this._resourceTagCollection = new ResourceTagCollection({
- allowedTags: Object.values(GLOBAL_TAGS)
- });
- this.options = options;
- }
-
- getRootProject() {
- return this.rootProject;
- }
-
- getOption(key) {
- return this.options[key];
- }
-
- createProjectContext({project, resources}) {
- const projectBuildContext = new ProjectBuildContext({
- buildContext: this,
- globalTags: GLOBAL_TAGS,
- project,
- resources
- });
- this.projectBuildContexts.push(projectBuildContext);
- return projectBuildContext;
- }
-
- async executeCleanupTasks() {
- await Promise.all(this.projectBuildContexts.map((ctx) => {
- return ctx.executeCleanupTasks();
- }));
- }
-
- getResourceTagCollection() {
- return this._resourceTagCollection;
- }
-}
-
-module.exports = BuildContext;
diff --git a/lib/builder/ProjectBuildContext.js b/lib/builder/ProjectBuildContext.js
deleted file mode 100644
index 5f8bc5ab1..000000000
--- a/lib/builder/ProjectBuildContext.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const ResourceTagCollection = require("@ui5/fs").ResourceTagCollection;
-
-// Note: When adding standard tags, always update the public documentation in TaskUtil
-// (Type "module:@ui5/builder.tasks.TaskUtil~StandardBuildTags")
-const STANDARD_TAGS = {
- OmitFromBuildResult: "ui5:OmitFromBuildResult",
- IsBundle: "ui5:IsBundle",
-};
-
-/**
- * Build context of a single project. Always part of an overall
- * [Build Context]{@link module:@ui5/builder.builder.BuildContext}
- *
- * @private
- * @memberof module:@ui5/builder.builder
- */
-class ProjectBuildContext {
- constructor({buildContext, globalTags, project, resources}) {
- if (!buildContext || !globalTags || !project || !resources) {
- throw new Error(`One or more mandatory parameters are missing`);
- }
- this._buildContext = buildContext;
- this._project = project;
- // this.resources = resources;
- this.queues = {
- cleanup: []
- };
-
- this.STANDARD_TAGS = Object.assign({}, STANDARD_TAGS, globalTags);
- Object.freeze(this.STANDARD_TAGS);
-
- this._resourceTagCollection = new ResourceTagCollection({
- allowedTags: Object.values(this.STANDARD_TAGS),
- superCollection: this._buildContext.getResourceTagCollection()
- });
- }
-
- isRootProject() {
- return this._project === this._buildContext.getRootProject();
- }
-
- getOption(key) {
- return this._buildContext.getOption(key);
- }
-
- registerCleanupTask(callback) {
- this.queues.cleanup.push(callback);
- }
-
- async executeCleanupTasks() {
- await Promise.all(this.queues.cleanup.map((callback) => {
- return callback();
- }));
- }
-
- getResourceTagCollection() {
- return this._resourceTagCollection;
- }
-}
-
-module.exports = ProjectBuildContext;
diff --git a/lib/builder/builder.js b/lib/builder/builder.js
deleted file mode 100644
index e32137529..000000000
--- a/lib/builder/builder.js
+++ /dev/null
@@ -1,425 +0,0 @@
-const {promisify} = require("util");
-const rimraf = promisify(require("rimraf"));
-const log = require("@ui5/logger").getGroupLogger("builder:builder");
-const resourceFactory = require("@ui5/fs").resourceFactory;
-const MemAdapter = require("@ui5/fs").adapters.Memory;
-const typeRepository = require("../types/typeRepository");
-const taskRepository = require("../tasks/taskRepository");
-const BuildContext = require("./BuildContext");
-
-
-// Set of tasks for development
-const devTasks = [
- "replaceCopyright",
- "replaceVersion",
- "replaceBuildtime",
- "buildThemes"
-];
-
-/**
- * Calculates the elapsed build time and returns a prettified output
- *
- * @private
- * @param {Array} startTime Array provided by process.hrtime()
- * @returns {string} Difference between now and the provided time array as formatted string
- */
-function getElapsedTime(startTime) {
- const prettyHrtime = require("pretty-hrtime");
- const timeDiff = process.hrtime(startTime);
- return prettyHrtime(timeDiff);
-}
-
-/**
- * Creates the list of tasks to be executed by the build process
- *
- * Sets specific tasks to be disabled by default, these tasks need to be included explicitly.
- * Based on the selected build mode (dev|selfContained|preload), different tasks are enabled.
- * Tasks can be enabled or disabled. The wildcard * is also supported and affects all tasks.
- *
- * @private
- * @param {object} parameters
- * @param {boolean} parameters.dev Sets development mode, which only runs essential tasks
- * @param {boolean} parameters.selfContained
- * True if a the build should be self-contained or false for prelead build bundles
- * @param {boolean} parameters.jsdoc True if a JSDoc build should be executed
- * @param {Array} parameters.includedTasks Task list to be included from build
- * @param {Array} parameters.excludedTasks Task list to be excluded from build
- * @returns {Array} Return a task list for the builder
- */
-function composeTaskList({dev, selfContained, jsdoc, includedTasks, excludedTasks}) {
- const definedTasks = taskRepository.getAllTaskNames();
- let selectedTasks = definedTasks.reduce((list, key) => {
- list[key] = true;
- return list;
- }, {});
-
- // Exclude non default tasks
- selectedTasks.generateManifestBundle = false;
- selectedTasks.generateStandaloneAppBundle = false;
- selectedTasks.transformBootstrapHtml = false;
- selectedTasks.generateJsdoc = false;
- selectedTasks.executeJsdocSdkTransformation = false;
- selectedTasks.generateCachebusterInfo = false;
- selectedTasks.generateApiIndex = false;
- selectedTasks.generateThemeDesignerResources = false;
-
- // Disable generateResourcesJson due to performance.
- // When executed it analyzes each module's AST and therefore
- // takes up much time (~10% more)
- selectedTasks.generateResourcesJson = false;
-
- if (selfContained) {
- // No preloads, bundle only
- selectedTasks.generateComponentPreload = false;
- selectedTasks.generateStandaloneAppBundle = true;
- selectedTasks.transformBootstrapHtml = true;
- selectedTasks.generateLibraryPreload = false;
- }
-
- // TODO 3.0: exclude generateVersionInfo if not --all is used
-
- if (jsdoc) {
- // Include JSDoc tasks
- selectedTasks.generateJsdoc = true;
- selectedTasks.executeJsdocSdkTransformation = true;
- selectedTasks.generateApiIndex = true;
-
- // Include theme build as required for SDK
- selectedTasks.buildThemes = true;
-
- // Exclude all tasks not relevant to JSDoc generation
- selectedTasks.replaceCopyright = false;
- selectedTasks.replaceVersion = false;
- selectedTasks.replaceBuildtime = false;
- selectedTasks.generateComponentPreload = false;
- selectedTasks.generateLibraryPreload = false;
- selectedTasks.generateLibraryManifest = false;
- selectedTasks.minify = false;
- selectedTasks.generateFlexChangesBundle = false;
- selectedTasks.generateManifestBundle = false;
- }
-
- // Only run essential tasks in development mode, it is not desired to run time consuming tasks during development.
- if (dev) {
- // Overwrite all other tasks with noop promise
- Object.keys(selectedTasks).forEach((key) => {
- if (devTasks.indexOf(key) === -1) {
- selectedTasks[key] = false;
- }
- });
- }
-
- // Exclude tasks
- for (let i = 0; i < excludedTasks.length; i++) {
- const taskName = excludedTasks[i];
- if (taskName === "*") {
- Object.keys(selectedTasks).forEach((sKey) => {
- selectedTasks[sKey] = false;
- });
- break;
- }
- if (selectedTasks[taskName] === true) {
- selectedTasks[taskName] = false;
- } else if (typeof selectedTasks[taskName] === "undefined") {
- log.warn(`Unable to exclude task '${taskName}': Task is unknown`);
- }
- }
-
- // Include tasks
- for (let i = 0; i < includedTasks.length; i++) {
- const taskName = includedTasks[i];
- if (taskName === "*") {
- Object.keys(selectedTasks).forEach((sKey) => {
- selectedTasks[sKey] = true;
- });
- break;
- }
- if (selectedTasks[taskName] === false) {
- selectedTasks[taskName] = true;
- } else if (typeof selectedTasks[taskName] === "undefined") {
- log.warn(`Unable to include task '${taskName}': Task is unknown`);
- }
- }
-
- // Filter only for tasks that will be executed
- selectedTasks = Object.keys(selectedTasks).filter((task) => selectedTasks[task]);
-
- return selectedTasks;
-}
-
-async function executeCleanupTasks(buildContext) {
- log.info("Executing cleanup tasks...");
- await buildContext.executeCleanupTasks();
-}
-
-function registerCleanupSigHooks(buildContext) {
- function createListener(exitCode) {
- return function() {
- // Asynchronously cleanup resources, then exit
- executeCleanupTasks(buildContext).then(() => {
- process.exit(exitCode);
- });
- };
- }
-
- const processSignals = {
- "SIGHUP": createListener(128 + 1),
- "SIGINT": createListener(128 + 2),
- "SIGTERM": createListener(128 + 15),
- "SIGBREAK": createListener(128 + 21)
- };
-
- for (const signal of Object.keys(processSignals)) {
- process.on(signal, processSignals[signal]);
- }
-
- // == TO BE DISCUSSED: Also cleanup for unhandled rejections and exceptions?
- // Add additional events like signals since they are registered on the process
- // event emitter in a similar fashion
- // processSignals["unhandledRejection"] = createListener(1);
- // process.once("unhandledRejection", processSignals["unhandledRejection"]);
- // processSignals["uncaughtException"] = function(err, origin) {
- // const fs = require("fs");
- // fs.writeSync(
- // process.stderr.fd,
- // `Caught exception: ${err}\n` +
- // `Exception origin: ${origin}`
- // );
- // createListener(1)();
- // };
- // process.once("uncaughtException", processSignals["uncaughtException"]);
-
- return processSignals;
-}
-
-function deregisterCleanupSigHooks(signals) {
- for (const signal of Object.keys(signals)) {
- process.removeListener(signal, signals[signal]);
- }
-}
-
-/**
- * Builder
- *
- * @public
- * @namespace
- * @alias module:@ui5/builder.builder
- */
-module.exports = {
- /**
- * Configures the project build and starts it.
- *
- * @public
- * @param {object} parameters Parameters
- * @param {object} parameters.tree Project tree as generated by the
- * [@ui5/project.normalizer]{@link module:@ui5/project.normalizer}
- * @param {string} parameters.destPath Target path
- * @param {boolean} [parameters.cleanDest=false] Decides whether project should clean the target path before build
- * @param {boolean} [parameters.buildDependencies=false] Decides whether project dependencies are built as well
- * @param {Array.} [parameters.includedDependencies=[]]
- * List of build dependencies to be included if buildDependencies is true
- * @param {Array.} [parameters.excludedDependencies=[]]
- * List of build dependencies to be excluded if buildDependencies is true.
- * If the wildcard '*' is provided, only the included dependencies will be built.
- * @param {boolean} [parameters.dev=false]
- * Decides whether a development build should be activated (skips non-essential and time-intensive tasks)
- * @param {boolean} [parameters.selfContained=false] Flag to activate self contained build
- * @param {boolean} [parameters.cssVariables=false] Flag to activate CSS variables generation
- * @param {boolean} [parameters.jsdoc=false] Flag to activate JSDoc build
- * @param {Array.} [parameters.includedTasks=[]] List of tasks to be included
- * @param {Array.} [parameters.excludedTasks=[]] List of tasks to be excluded.
- * If the wildcard '*' is provided, only the included tasks will be executed.
- * @param {Array.} [parameters.devExcludeProject=[]] List of projects to be excluded from development build
- * @returns {Promise} Promise resolving to undefined once build has finished
- */
- async build({
- tree, destPath, cleanDest = false,
- buildDependencies = false, includedDependencies = [], excludedDependencies = [],
- dev = false, selfContained = false, cssVariables = false, jsdoc = false,
- includedTasks = [], excludedTasks = [], devExcludeProject = []
- }) {
- const startTime = process.hrtime();
- log.info(`Building project ${tree.metadata.name}` + (buildDependencies ? "" : " not") +
- " including dependencies..." + (dev ? " [dev mode]" : ""));
- log.verbose(`Building to ${destPath}...`);
-
- const selectedTasks = composeTaskList({dev, selfContained, jsdoc, includedTasks, excludedTasks});
-
- const fsTarget = resourceFactory.createAdapter({
- fsBasePath: destPath,
- virBasePath: "/"
- });
-
- const buildContext = new BuildContext({
- rootProject: tree,
- options: {
- cssVariables: cssVariables
- }
- });
- const cleanupSigHooks = registerCleanupSigHooks(buildContext);
-
- const projects = {}; // Unique project index to prevent building the same project multiple times
- const projectWriters = {}; // Collection of memory adapters of already built libraries
- function projectFilter(project) {
- function projectMatchesAny(deps) {
- return deps.some((dep) => dep instanceof RegExp ?
- dep.test(project.metadata.name) : dep === project.metadata.name);
- }
-
- // if everything is included, this overrules exclude lists
- if (includedDependencies.includes("*")) return true;
- let test = !excludedDependencies.includes("*"); // exclude everything?
-
- if (test && projectMatchesAny(excludedDependencies)) {
- test = false;
- }
- if (!test && projectMatchesAny(includedDependencies)) {
- test = true;
- }
-
- return test;
- }
-
- const projectCountMarker = {};
- function projectCount(project, count = 0) {
- if (buildDependencies) {
- count = project.dependencies.filter(projectFilter).reduce((depCount, depProject) => {
- return projectCount(depProject, depCount);
- }, count);
- }
- if (!projectCountMarker[project.metadata.name]) {
- count++;
- projectCountMarker[project.metadata.name] = true;
- }
- return count;
- }
- const buildLogger = log.createTaskLogger("🛠 ", projectCount(tree));
-
- function buildProject(project) {
- const projectBasePath = `/resources/${project.metadata.namespace}`;
- let depPromise;
- let projectTasks = selectedTasks;
-
- // Build dependencies in sequence as it is far easier to detect issues and reduces
- // side effects or other issues such as too many open files
- if (buildDependencies) {
- depPromise = project.dependencies.filter(projectFilter).reduce(function(p, depProject) {
- return p.then(() => buildProject(depProject));
- }, Promise.resolve());
- } else {
- depPromise = Promise.resolve();
- }
-
- // Build the project after all dependencies have been built
- return depPromise.then(() => {
- if (projects[project.metadata.name]) {
- return Promise.resolve();
- } else {
- projects[project.metadata.name] = true;
- }
- buildLogger.startWork(`Building project ${project.metadata.name}`);
-
- const projectType = typeRepository.getType(project.type);
- const resourceCollections = resourceFactory.createCollectionsForTree(project, {
- virtualReaders: projectWriters,
- getVirtualBasePathPrefix: function({project, virBasePath}) {
- if (project.type === "application" && project.metadata.namespace) {
- return projectBasePath;
- }
- },
- getProjectExcludes: function(project) {
- if (project.builder && project.builder.resources) {
- return project.builder.resources.excludes;
- }
- }
- });
-
- const writer = new MemAdapter({
- virBasePath: "/"
- });
- // Store project writer as virtual reader for parent projects
- // so they can access the build results of this project
- projectWriters[project.metadata.name] = writer;
-
- // TODO: Add getter for writer of DuplexColection
- const workspace = resourceFactory.createWorkspace({
- virBasePath: "/",
- writer,
- reader: resourceCollections.source,
- name: project.metadata.name
- });
-
- const projectContext = buildContext.createProjectContext({
- project, // TODO 2.0: Add project facade object/instance here
- resources: {
- workspace,
- dependencies: resourceCollections.dependencies
- }
- });
-
- const TaskUtil = require("../tasks/TaskUtil");
- const taskUtil = new TaskUtil({
- projectBuildContext: projectContext
- });
-
- if (dev && devExcludeProject.indexOf(project.metadata.name) !== -1) {
- projectTasks = composeTaskList({dev: false, selfContained, includedTasks, excludedTasks});
- }
-
- return projectType.build({
- resourceCollections: {
- workspace,
- dependencies: resourceCollections.dependencies
- },
- tasks: projectTasks,
- project,
- parentLogger: log,
- taskUtil
- }).then(() => {
- log.verbose("Finished building project %s. Writing out files...", project.metadata.name);
- buildLogger.completeWork(1);
-
- return workspace.byGlob("/**/*").then((resources) => {
- const tagCollection = projectContext.getResourceTagCollection();
- return Promise.all(resources.map((resource) => {
- if (tagCollection.getTag(resource, projectContext.STANDARD_TAGS.OmitFromBuildResult)) {
- log.verbose(`Skipping write of resource tagged as "OmitFromBuildResult": ` +
- resource.getPath());
- return; // Skip target write for this resource
- }
- if (projectContext.isRootProject() && project.type === "application" &&
- project.metadata.namespace) {
- // Root-application projects only: Remove namespace prefix if given
- const resourcePath = resource.getPath();
- if (resourcePath.startsWith(projectBasePath)) {
- resource.setPath(resourcePath.replace(projectBasePath, ""));
- }
- }
- return fsTarget.write(resource);
- }));
- });
- });
- });
- }
-
- try {
- if (cleanDest) {
- await rimraf(destPath);
- }
- await buildProject(tree);
- log.info(`Build succeeded in ${getElapsedTime(startTime)}`);
- } catch (err) {
- log.error(`Build failed in ${getElapsedTime(startTime)}`);
- throw err;
- } finally {
- deregisterCleanupSigHooks(cleanupSigHooks);
- await executeCleanupTasks(buildContext);
- }
- }
-};
-
-// Export local function for testing only
-/* istanbul ignore else */
-if (process.env.NODE_ENV === "test") {
- module.exports._composeTaskList = composeTaskList;
-}
diff --git a/lib/lbt/bundle/Builder.js b/lib/lbt/bundle/Builder.js
index 687c2d3b8..969c43f59 100644
--- a/lib/lbt/bundle/Builder.js
+++ b/lib/lbt/bundle/Builder.js
@@ -594,6 +594,18 @@ class BundleBuilder {
if (moduleSourceMap) {
moduleSourceMap = JSON.parse(moduleSourceMap);
+
+ // Check for index map, which is currently not supported
+ if (Array.isArray(moduleSourceMap.sections)) {
+ log.warn(
+ `Module ${moduleName} references an index source map which is currently not supported. ` +
+ `A transient source map will be created instead...`
+ );
+ moduleSourceMap = createTransientSourceMap({
+ moduleName: path.posix.basename(resourcePath),
+ moduleContent
+ });
+ }
} else {
log.verbose(`No source map available for module ${moduleName}. Creating transient source map...`);
moduleSourceMap = createTransientSourceMap({
diff --git a/lib/lbt/resources/LocatorResource.js b/lib/lbt/resources/LocatorResource.js
index cb73c721e..67bf28e11 100644
--- a/lib/lbt/resources/LocatorResource.js
+++ b/lib/lbt/resources/LocatorResource.js
@@ -11,7 +11,7 @@ class LocatorResource extends Resource {
}
getProject() {
- return this.resource._project;
+ return this.resource.getProject();
}
getPath() {
diff --git a/lib/lbt/utils/escapePropertiesFile.js b/lib/lbt/utils/escapePropertiesFile.js
index 5139d035e..8b5eb5329 100644
--- a/lib/lbt/utils/escapePropertiesFile.js
+++ b/lib/lbt/utils/escapePropertiesFile.js
@@ -4,7 +4,7 @@ const nonAsciiEscaper = require("../../processors/nonAsciiEscaper");
* Can be used to escape *.properties files.
*
* Input encoding is read from project configuration.
- * In case the resource belongs to no project (e.g. bundler is used standalone) the default is "ISO-8859-1".
+ * In case the resource belongs to no project (e.g. bundler is used standalone) the default is "UTF-8".
*
* @private
* @param {Resource} resource the resource for which the content will be escaped
@@ -12,13 +12,10 @@ const nonAsciiEscaper = require("../../processors/nonAsciiEscaper");
*/
module.exports = async function(resource) {
const project = resource.getProject();
- let propertiesFileSourceEncoding = project &&
- project.resources &&
- project.resources.configuration &&
- project.resources.configuration.propertiesFileSourceEncoding;
+ let propertiesFileSourceEncoding = project && project.getPropertiesFileSourceEncoding();
if (!propertiesFileSourceEncoding) {
- if (project && ["0.1", "1.0", "1.1"].includes(project.specVersion)) {
+ if (project && ["0.1", "1.0", "1.1"].includes(project.getSpecVersion())) {
// default encoding to "ISO-8859-1" for old specVersions
propertiesFileSourceEncoding = "ISO-8859-1";
} else {
diff --git a/lib/processors/bundlers/moduleBundler.js b/lib/processors/bundlers/moduleBundler.js
index ff643cd8b..982a85698 100644
--- a/lib/processors/bundlers/moduleBundler.js
+++ b/lib/processors/bundlers/moduleBundler.js
@@ -148,7 +148,6 @@ module.exports = function({resources, options: {bundleDefinition, bundleOptions,
log.verbose(`bundleDefinition: ${JSON.stringify(bundleDefinition, null, 2)}`);
log.verbose(`bundleOptions: ${JSON.stringify(bundleOptions, null, 2)}`);
}
-
return pool.prepare( resources, moduleNameMapping ).
then( () => builder.createBundle(bundleDefinition, bundleOptions) ).
then( (results) => {
diff --git a/lib/processors/manifestCreator.js b/lib/processors/manifestCreator.js
index db2a37c63..463c109b5 100644
--- a/lib/processors/manifestCreator.js
+++ b/lib/processors/manifestCreator.js
@@ -157,7 +157,9 @@ class LibraryBundle {
/*
* Creates the library manifest.json file for a UILibrary.
*/
-async function createManifest(libraryResource, libBundle, descriptorVersion, _include3rdParty, omitMinVersions) {
+async function createManifest(
+ libraryResource, libBundle, descriptorVersion, _include3rdParty, omitMinVersions, getProjectVersion
+) {
// create a Library wrapper around the .library XML
const library = await Library.from(libraryResource);
@@ -223,13 +225,6 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
return version && version !== "@version@" && version !== "${version}";
}
- function getProjectVersion() {
- const project = libraryResource._project;
- if ( project ) {
- return project.version;
- }
- }
-
function getLibraryTitle() {
if ( library.getTitle() ) {
return library.getTitle();
@@ -321,7 +316,7 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
embeds: findEmbeddedComponents(),
i18n,
applicationVersion: {
- version: isValid(library.getVersion()) ? library.getVersion() : getProjectVersion()
+ version: isValid(library.getVersion()) ? library.getVersion() : getProjectVersion(library.getName())
},
title: getLibraryTitle(),
description: library.getDocumentation(),
@@ -393,12 +388,7 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
function createSapUI5() {
function getUI5Version() {
- const dummy = new Dependency({
- libraryName: [{
- _: "sap.ui.core"
- }]
- });
- return normalizeVersion(getVersion(dummy));
+ return normalizeVersion(getProjectVersion("sap.ui.core"));
}
function dependencies() {
@@ -410,7 +400,7 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
if ( library.getDependencies() != null ) {
for (const dep of library.getDependencies()) {
dependencies.libs[dep.getLibraryName()] = {
- minVersion: omitMinVersions ? "" : getVersion(dep),
+ minVersion: omitMinVersions ? "" : getProjectVersion(dep.getLibraryName()),
lazy: dep.isLazy() || undefined // suppress default (false)
};
}
@@ -619,33 +609,6 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
return v.major + "." + v.minor;
}
- function getVersion(dependency) {
- const version = dependency.getVersion();
- if ( version != null ) {
- return version;
- }
-
- function hasName(entity) {
- return entity.metadata && entity.metadata.name === dependency.getLibraryName();
- }
-
- const project = libraryResource._project;
- if ( project ) {
- if ( Array.isArray(project.dependencies) ) {
- const lib = project.dependencies.find(hasName);
- if ( lib ) {
- return lib.version;
- }
- }
- if ( hasName(project) ) {
- return project.version;
- }
- }
-
- throw new Error(
- `Couldn't find version for library '${dependency.getLibraryName()}', project dependency missing?`);
- }
-
return {
"_version": descriptorVersion.toString(),
"sap.app": createSapApp(),
@@ -657,7 +620,7 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
};
}
-module.exports = function({libraryResource, resources, options}) {
+module.exports = function({libraryResource, resources, getProjectVersion, options}) {
// merge options with defaults
options = Object.assign({
descriptorVersion: APP_DESCRIPTOR_V22, // TODO 3.0: change this to type string instead of a semver object
@@ -677,7 +640,7 @@ module.exports = function({libraryResource, resources, options}) {
}
return createManifest(libraryResource, libBundle, options.descriptorVersion, options.include3rdParty,
- options.omitMinVersions)
+ options.omitMinVersions, getProjectVersion)
.then((manifest) => {
return new EvoResource({
path: resourcePathPrefix + "manifest.json",
diff --git a/lib/tasks/TaskUtil.js b/lib/tasks/TaskUtil.js
index f751617c5..6e6a3a717 100644
--- a/lib/tasks/TaskUtil.js
+++ b/lib/tasks/TaskUtil.js
@@ -19,9 +19,15 @@ class TaskUtil {
* @public
* @typedef {object} module:@ui5/builder.tasks.TaskUtil~StandardBuildTags
* @property {string} OmitFromBuildResult
- * Setting this tag to true for a resource will prevent it from being written to the build target
+ * Setting this tag to true will prevent the resource from being written to the build target directory
* @property {string} IsBundle
* This tag identifies resources that contain (i.e. bundle) multiple other resources
+ * @property {string} IsDebugVariant
+ * This tag identifies resources that are a debug variant (typically named with a "-dbg" suffix)
+ * of another resource. This tag is part of the build manifest.
+ * @property {string} HasDebugVariant
+ * This tag identifies resources for which a debug variant has been created.
+ * This tag is part of the build manifest.
*/
/**
@@ -34,12 +40,22 @@ class TaskUtil {
*/
constructor({projectBuildContext}) {
this._projectBuildContext = projectBuildContext;
-
/**
* @member {module:@ui5/builder.tasks.TaskUtil~StandardBuildTags}
* @public
*/
- this.STANDARD_TAGS = this._projectBuildContext.STANDARD_TAGS;
+ this.STANDARD_TAGS = Object.freeze({
+ // "Project" tags:
+ // Will be stored on project instance and are hence part of the build manifest
+ IsDebugVariant: "ui5:IsDebugVariant",
+ HasDebugVariant: "ui5:HasDebugVariant",
+
+ // "Build" tags:
+ // Will be stored on the project build context
+ // They are only available to the build tasks of a single project
+ OmitFromBuildResult: "ui5:OmitFromBuildResult",
+ IsBundle: "ui5:IsBundle"
+ });
}
/**
@@ -51,14 +67,20 @@ class TaskUtil {
* This method is only available to custom task extensions defining
* Specification Version 2.2 and above.
*
- * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be stored for
+ * @param {module:@ui5/fs.Resource} resource Resource-instance the tag should be stored for
* @param {string} tag Name of the tag.
* Currently only the [STANDARD_TAGS]{@link module:@ui5/builder.tasks.TaskUtil#STANDARD_TAGS} are allowed
* @param {string|boolean|integer} [value=true] Tag value. Must be primitive
* @public
*/
- setTag(resourcePath, tag, value) {
- return this._projectBuildContext.getResourceTagCollection().setTag(resourcePath, tag, value);
+ setTag(resource, tag, value) {
+ if (typeof resource === "string") {
+ throw new Error("Deprecated parameter: " +
+ "Since UI5 Tooling 3.0, #setTag requires a resource instance. Strings are no longer accepted");
+ }
+
+ const collection = this._projectBuildContext.getResourceTagCollection(resource, tag);
+ return collection.setTag(resource, tag, value);
}
/**
@@ -68,14 +90,19 @@ class TaskUtil {
* This method is only available to custom task extensions defining
* Specification Version 2.2 and above.
*
- * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be retrieved for
+ * @param {module:@ui5/fs.Resource} resource Resource-instance the tag should be retrieved for
* @param {string} tag Name of the tag
* @returns {string|boolean|integer|undefined} Tag value for the given resource.
* undefined if no value is available
* @public
*/
- getTag(resourcePath, tag) {
- return this._projectBuildContext.getResourceTagCollection().getTag(resourcePath, tag);
+ getTag(resource, tag) {
+ if (typeof resource === "string") {
+ throw new Error("Deprecated parameter: " +
+ "Since UI5 Tooling 3.0, #getTag requires a resource instance. Strings are no longer accepted");
+ }
+ const collection = this._projectBuildContext.getResourceTagCollection(resource, tag);
+ return collection.getTag(resource, tag);
}
/**
@@ -86,12 +113,17 @@ class TaskUtil {
* This method is only available to custom task extensions defining
* Specification Version 2.2 and above.
*
- * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be cleared for
+ * @param {module:@ui5/fs.Resource} resource Resource-instance the tag should be cleared for
* @param {string} tag Tag
* @public
*/
- clearTag(resourcePath, tag) {
- return this._projectBuildContext.getResourceTagCollection().clearTag(resourcePath, tag);
+ clearTag(resource, tag) {
+ if (typeof resource === "string") {
+ throw new Error("Deprecated parameter: " +
+ "Since UI5 Tooling 3.0, #clearTag requires a resource instance. Strings are no longer accepted");
+ }
+ const collection = this._projectBuildContext.getResourceTagCollection(resource, tag);
+ return collection.clearTag(resource, tag);
}
/**
@@ -136,6 +168,22 @@ class TaskUtil {
return this._projectBuildContext.registerCleanupTask(callback);
}
+ /**
+ * Retrieve a single project from the dependency graph
+ *
+ *
+ * This method is only available to custom task extensions defining
+ * Specification Version 2.7 and above.
+ *
+ * @param {string} projectName Name of the project to retrieve
+ * @returns {module:@ui5/project.specifications.Project|undefined}
+ * project instance or undefined if the project is unknown to the graph
+ * @public
+ */
+ getProject(projectName) {
+ return this._projectBuildContext.getProject(projectName);
+ }
+
/**
* Get an interface to an instance of this class that only provides those functions
* that are supported by the given custom task extension specification version.
@@ -150,12 +198,10 @@ class TaskUtil {
const baseInterface = {
STANDARD_TAGS: this.STANDARD_TAGS,
- setTag: this.setTag.bind(this),
- clearTag: this.clearTag.bind(this),
- getTag: this.getTag.bind(this),
- isRootProject: this.isRootProject.bind(this),
- registerCleanupTask: this.registerCleanupTask.bind(this)
};
+ bindFunctions(this, baseInterface, [
+ "setTag", "clearTag", "getTag", "isRootProject", "registerCleanupTask"
+ ]);
switch (specVersion) {
case "2.2":
case "2.3":
@@ -163,10 +209,29 @@ class TaskUtil {
case "2.5":
case "2.6":
return baseInterface;
+ case "2.7":
+ baseInterface.getProject = (projectName) => {
+ const project = this.getProject(projectName);
+ const baseProjectInterface = {};
+ bindFunctions(project, baseProjectInterface, [
+ "getName", "getVersion", "getNamespace"
+ ]);
+ switch (specVersion) {
+ case "2.7":
+ return baseProjectInterface;
+ }
+ };
+ return baseInterface;
default:
throw new Error(`TaskUtil: Unknown or unsupported Specification Version ${specVersion}`);
}
}
}
+function bindFunctions(sourceObject, targetObject, funcNames) {
+ funcNames.forEach((funcName) => {
+ targetObject[funcName] = sourceObject[funcName].bind(sourceObject);
+ });
+}
+
module.exports = TaskUtil;
diff --git a/lib/tasks/bundlers/generateFlexChangesBundle.js b/lib/tasks/bundlers/generateFlexChangesBundle.js
index 3bd3b37be..9f58ebc79 100644
--- a/lib/tasks/bundlers/generateFlexChangesBundle.js
+++ b/lib/tasks/bundlers/generateFlexChangesBundle.js
@@ -2,6 +2,7 @@ const log = require("@ui5/logger").getLogger("builder:tasks:bundlers:generateFle
const flexChangesBundler = require("../../processors/bundlers/flexChangesBundler");
const semver = require("semver");
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Task to create changesBundle.json file containing all changes stored in the /changes folder for easier consumption
* at runtime.
@@ -16,10 +17,13 @@ const semver = require("semver");
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
* @param {module:@ui5/builder.tasks.TaskUtil|object} [parameters.taskUtil] TaskUtil
* @param {object} [parameters.options] Options
- * @param {string} [parameters.options.namespace] Application Namespace
+ * @param {string} [parameters.options.projectNamespace] Project Namespace
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = async function({workspace, taskUtil, options: {namespace}}) {
+module.exports = async function({workspace, taskUtil, options = {}}) {
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
// Use the given namespace if available, otherwise use no namespace
// (e.g. in case no manifest.json is present)
let pathPrefix = "";
diff --git a/lib/tasks/bundlers/generateLibraryPreload.js b/lib/tasks/bundlers/generateLibraryPreload.js
index 97234eb8d..b24c1b02e 100644
--- a/lib/tasks/bundlers/generateLibraryPreload.js
+++ b/lib/tasks/bundlers/generateLibraryPreload.js
@@ -223,19 +223,19 @@ function getSapUiCoreBunDef(name, filters, preload) {
* @alias module:@ui5/builder.tasks.generateLibraryPreload
* @param {object} parameters Parameters
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
- * @param {module:@ui5/builder.tasks.TaskUtil|object} [parameters.taskUtil] TaskUtil
+ * @param {module:@ui5/builder.tasks.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.
- * @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
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = function({workspace, taskUtil, options: {projectName, skipBundles = [], excludes = []}}) {
+module.exports = function({workspace, taskUtil, options: {skipBundles = [], excludes = [], projectName}}) {
let nonDbgWorkspace = workspace;
if (taskUtil) {
nonDbgWorkspace = workspace.filter(function(resource) {
@@ -260,14 +260,12 @@ module.exports = function({workspace, taskUtil, options: {projectName, skipBundl
// 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") {
- const coreProject = resources[0]._project;
- const coreSpecVersion = coreProject && coreProject.specVersion;
// 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 (!coreSpecVersion || ["0.1", "1.1", "2.0"].includes(coreSpecVersion)) {
+ if (!taskUtil || ["0.1", "1.1", "2.0"].includes(taskUtil.getProject().getSpecVersion())) {
const isEvo = resources.find((resource) => {
return resource.getPath() === "/resources/ui5loader.js";
});
@@ -345,7 +343,8 @@ module.exports = function({workspace, taskUtil, options: {projectName, skipBundl
} else {
// Fallback to "library.js" as library indicator
log.verbose(
- `Could not find a ".library" file for project ${projectName}, falling back to "library.js".`);
+ `Could not find a ".library" file for project ${projectName}, ` +
+ `falling back to "library.js".`);
return workspace.byGlob("/resources/**/library.js");
}
}).then((libraryIndicatorResources) => {
diff --git a/lib/tasks/bundlers/generateManifestBundle.js b/lib/tasks/bundlers/generateManifestBundle.js
index 38718e2c3..5bbdc659e 100644
--- a/lib/tasks/bundlers/generateManifestBundle.js
+++ b/lib/tasks/bundlers/generateManifestBundle.js
@@ -4,14 +4,7 @@ const DESCRIPTOR = "manifest.json";
const PROPERTIES_EXT = ".properties";
const BUNDLE_NAME = "manifest-bundle.zip";
-/**
- *
- * @public
- * @typedef {object} ManifestBundlerOptions
- * @property {string} projectName Project Name
- * @property {string} namespace Namespace
- */
-
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Task for manifestBundler.
*
@@ -19,11 +12,16 @@ const BUNDLE_NAME = "manifest-bundle.zip";
* @alias module:@ui5/builder.tasks.generateManifestBundle
* @param {object} parameters Parameters
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
- * @param {ManifestBundlerOptions} parameters.options Options
+ * @param {object} parameters.options Options
+ * @param {string} parameters.options.projectName Project name
+ * @param {string} parameters.options.projectNamespace Project namespace
* @returns {Promise} Promise resolving with undefined once data has been written
*/
module.exports = async function({workspace, options = {}}) {
- const {projectName, namespace} = options;
+ const {projectName} = options;
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
if (!projectName || !namespace) {
throw new Error("[generateManifestBundle]: One or more mandatory options not provided");
}
diff --git a/lib/tasks/bundlers/generateStandaloneAppBundle.js b/lib/tasks/bundlers/generateStandaloneAppBundle.js
index 59ad6c0c2..adef5cfe3 100644
--- a/lib/tasks/bundlers/generateStandaloneAppBundle.js
+++ b/lib/tasks/bundlers/generateStandaloneAppBundle.js
@@ -58,6 +58,7 @@ function getBundleDefinition(config) {
return bundleDefinition;
}
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Task for bundling standalone applications.
*
@@ -69,10 +70,14 @@ function getBundleDefinition(config) {
* @param {module:@ui5/builder.tasks.TaskUtil|object} [parameters.taskUtil] TaskUtil
* @param {object} parameters.options Options
* @param {string} parameters.options.projectName Project name
- * @param {string} [parameters.options.namespace] Project namespace
+ * @param {string} [parameters.options.projectNamespace] Project namespace
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = async function({workspace, dependencies, taskUtil, options: {projectName, namespace}}) {
+module.exports = async function({workspace, dependencies, taskUtil, options}) {
+ const {projectName} = options;
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
if (!namespace) {
log.warn(`Namespace of project ${projectName} is not known. Self contained bundling is currently ` +
`unable to generate complete bundles for such projects.`);
diff --git a/lib/tasks/bundlers/utils/createModuleNameMapping.js b/lib/tasks/bundlers/utils/createModuleNameMapping.js
index 23544ab37..500963e14 100644
--- a/lib/tasks/bundlers/utils/createModuleNameMapping.js
+++ b/lib/tasks/bundlers/utils/createModuleNameMapping.js
@@ -17,8 +17,9 @@ const ModuleName = require("../../../lbt/utils/ModuleName");
module.exports = function({resources, taskUtil}) {
const moduleNameMapping = {};
for (let i = resources.length - 1; i >= 0; i--) {
- const resourcePath = resources[i].getPath();
- if (taskUtil.getTag(resourcePath, taskUtil.STANDARD_TAGS.IsDebugVariant)) {
+ const resource = resources[i];
+ if (taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant)) {
+ const resourcePath = resource.getPath();
const nonDbgPath = ModuleName.getNonDebugName(resourcePath);
if (!nonDbgPath) {
throw new Error(`Failed to resolve non-debug name for ${resourcePath}`);
diff --git a/lib/tasks/generateCachebusterInfo.js b/lib/tasks/generateCachebusterInfo.js
index d5d8d1b3a..b95b37578 100644
--- a/lib/tasks/generateCachebusterInfo.js
+++ b/lib/tasks/generateCachebusterInfo.js
@@ -28,6 +28,7 @@ function getSigner(type) {
}
}
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Task to generate the application cachebuster info file.
*
@@ -35,13 +36,16 @@ function getSigner(type) {
* @alias module:@ui5/builder.tasks.generateCachebusterInfo
* @param {object} parameters Parameters
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
- * @param {module:@ui5/fs.AbstractReader} parameters.dependencies Reader or Collection to read dependency files
* @param {object} parameters.options Options
- * @param {string} parameters.options.namespace Namespace of the application
+ * @param {string} parameters.options.projectNamespace Namespace of the application
* @param {string} [parameters.options.signatureType='time'] Type of signature to be used ('time' or 'hash')
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = function({workspace, dependencies, options: {namespace, signatureType}}) {
+module.exports = function({workspace, options}) {
+ const {signatureType} = options;
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
const basePath = `/resources/${namespace}/`;
return workspace.byGlob(`/resources/${namespace}/**/*`)
.then(async (resources) => {
diff --git a/lib/tasks/generateLibraryManifest.js b/lib/tasks/generateLibraryManifest.js
index 46d30c092..d225c8378 100644
--- a/lib/tasks/generateLibraryManifest.js
+++ b/lib/tasks/generateLibraryManifest.js
@@ -11,11 +11,12 @@ const manifestCreator = require("../processors/manifestCreator");
* @alias module:@ui5/builder.tasks.generateLibraryManifest
* @param {object} parameters Parameters
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
+ * @param {module:@ui5/builder.tasks.TaskUtil|object} [parameters.taskUtil] TaskUtil
* @param {object} parameters.options Options
* @param {string} parameters.options.projectName Project name
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = function({workspace, options: {projectName}}) {
+module.exports = function({workspace, taskUtil, options: {projectName}}) {
// Note:
// *.library files are needed to identify libraries
// *.json files are needed to avoid overwriting them
@@ -45,6 +46,9 @@ module.exports = function({workspace, options: {projectName}}) {
libraryResource: libraryIndicatorResource,
namespace: libraryNamespace,
resources,
+ getProjectVersion: (projectName) => {
+ return taskUtil?.getProject(projectName)?.getVersion();
+ },
options: {
}
}).then((manifest) => {
diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js
index f8f75c4fa..dbcfa1a84 100644
--- a/lib/tasks/generateThemeDesignerResources.js
+++ b/lib/tasks/generateThemeDesignerResources.js
@@ -187,6 +187,7 @@ async function generateCssVariablesLess({workspace, combo, namespace}) {
}
}
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Generates resources required for integration with the SAP Theme Designer.
*
@@ -198,11 +199,16 @@ async function generateCssVariablesLess({workspace, combo, namespace}) {
* @param {object} parameters.options Options
* @param {string} parameters.options.projectName Project name
* @param {string} parameters.options.version Project version
- * @param {string} [parameters.options.namespace] If the project is of type library, provide its namespace.
+ * @param {string} [parameters.options.projectNamespace] If the project is of type library,
+ * provide its namespace.
* Omit for type theme-library
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = async function({workspace, dependencies, options: {projectName, version, namespace}}) {
+module.exports = async function({workspace, dependencies, options}) {
+ const {projectName, version} = options;
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
// Skip sap.ui.documentation since it is not intended to be available in SAP Theme Designer to create custom themes
if (namespace === "sap/ui/documentation") {
return;
diff --git a/lib/tasks/generateVersionInfo.js b/lib/tasks/generateVersionInfo.js
index 03e34e309..4818105b5 100644
--- a/lib/tasks/generateVersionInfo.js
+++ b/lib/tasks/generateVersionInfo.js
@@ -19,7 +19,7 @@ module.exports = async ({workspace, dependencies, options: {rootProject, pattern
const resources = await dependencies.byGlob(pattern);
const libraryInfosPromises = resources.map((dotLibResource) => {
- const namespace = dotLibResource._project.metadata.namespace;
+ const namespace = dotLibResource.getProject().getNamespace();
// pass all required resources to the processor
// the processor will then filter
return dependencies.byGlob(`/resources/${namespace}/**/${MANIFEST_JSON}`).then((manifestResources) => {
@@ -31,8 +31,8 @@ module.exports = async ({workspace, dependencies, options: {rootProject, pattern
return {
libraryManifest,
embeddedManifests,
- name: dotLibResource._project.metadata.name,
- version: dotLibResource._project.version
+ name: dotLibResource.getProject().getName(),
+ version: dotLibResource.getProject().getVersion()
};
});
});
@@ -40,8 +40,8 @@ module.exports = async ({workspace, dependencies, options: {rootProject, pattern
const [versionInfoResource] = await versionInfoGenerator({
options: {
- rootProjectName: rootProject.metadata.name,
- rootProjectVersion: rootProject.version,
+ rootProjectName: rootProject.getName(),
+ rootProjectVersion: rootProject.getVersion(),
libraryInfos
}
});
diff --git a/lib/tasks/taskRepository.js b/lib/tasks/taskRepository.js
index 8e80e0e3f..69306757e 100644
--- a/lib/tasks/taskRepository.js
+++ b/lib/tasks/taskRepository.js
@@ -36,30 +36,18 @@ function getTask(taskName) {
try {
const task = require(taskInfo.path);
return {
- task,
- specVersion: taskInfo.specVersion
+ task
};
} catch (err) {
throw new Error(`taskRepository: Failed to require task module for ${taskName}: ${err.message}`);
}
}
-function addTask({name, specVersion, taskPath}) {
- if (taskInfos[name]) {
- throw new Error(`taskRepository: A task with the name ${name} has already been registered`);
- }
- taskInfos[name] = {
- path: taskPath,
- specVersion
- };
-}
-
function getAllTaskNames() {
return Object.keys(taskInfos);
}
module.exports = {
getTask,
- addTask,
getAllTaskNames
};
diff --git a/lib/tasks/transformBootstrapHtml.js b/lib/tasks/transformBootstrapHtml.js
index 0477757eb..2f0a4706b 100644
--- a/lib/tasks/transformBootstrapHtml.js
+++ b/lib/tasks/transformBootstrapHtml.js
@@ -1,6 +1,7 @@
const log = require("@ui5/logger").getLogger("builder:tasks:transformBootstrapHtml");
const bootstrapHtmlTransformer = require("../processors/bootstrapHtmlTransformer");
+/* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
/**
* Task for transforming the application bootstrap HTML file.
*
@@ -12,7 +13,11 @@ const bootstrapHtmlTransformer = require("../processors/bootstrapHtmlTransformer
* @param {string} [parameters.options.namespace] Project namespace
* @returns {Promise} Promise resolving with undefined once data has been written
*/
-module.exports = async function({workspace, options: {projectName, namespace}}) {
+module.exports = async function({workspace, options}) {
+ const {projectName} = options;
+ // Backward compatibility: "namespace" option got renamed to "projectNamespace"
+ const namespace = options.projectNamespace || options.namespace;
+
let indexPath;
if (namespace) {
indexPath = `/resources/${namespace}/index.html`;
diff --git a/lib/types/AbstractBuilder.js b/lib/types/AbstractBuilder.js
deleted file mode 100644
index abe6802c9..000000000
--- a/lib/types/AbstractBuilder.js
+++ /dev/null
@@ -1,270 +0,0 @@
-
-/**
- * Resource collections
- *
- * @public
- * @typedef module:@ui5/builder.BuilderResourceCollections
- * @property {module:@ui5/fs.DuplexCollection} workspace Workspace Resource
- * @property {module:@ui5/fs.ReaderCollection} dependencies Workspace Resource
- */
-
-/**
- * Base class for the builder implementation of a project type
- *
- * @abstract
- */
-class AbstractBuilder {
- /**
- * Constructor
- *
- * @param {object} parameters
- * @param {BuilderResourceCollections} parameters.resourceCollections Resource collections
- * @param {object} parameters.project Project configuration
- * @param {GroupLogger} parameters.parentLogger Logger to use
- * @param {object} parameters.taskUtil
- */
- constructor({resourceCollections, project, parentLogger, taskUtil}) {
- if (new.target === AbstractBuilder) {
- throw new TypeError("Class 'AbstractBuilder' is abstract");
- }
-
- this.project = project;
-
- this.log = parentLogger.createSubLogger(project.type + " " + project.metadata.name, 0.2);
- this.taskLog = this.log.createTaskLogger("🔨");
-
- this.tasks = {};
- this.taskExecutionOrder = [];
- this.addStandardTasks({
- resourceCollections,
- project,
- log: this.log,
- taskUtil
- });
- this.addCustomTasks({
- resourceCollections,
- project,
- taskUtil
- });
- }
-
- /**
- * Adds all standard tasks to execute
- *
- * @abstract
- * @protected
- * @param {object} parameters
- * @param {BuilderResourceCollections} parameters.resourceCollections Resource collections
- * @param {object} parameters.taskUtil
- * @param {object} parameters.project Project configuration
- * @param {object} parameters.log @ui5/logger logger instance
- */
- addStandardTasks({resourceCollections, project, log, taskUtil}) {
- throw new Error("Function 'addStandardTasks' is not implemented");
- }
-
- /**
- * Adds custom tasks to execute
- *
- * @private
- * @param {object} parameters
- * @param {BuilderResourceCollections} parameters.resourceCollections Resource collections
- * @param {object} parameters.taskUtil
- * @param {object} parameters.project Project configuration
- */
- addCustomTasks({resourceCollections, project, taskUtil}) {
- const projectCustomTasks = project.builder && project.builder.customTasks;
- if (!projectCustomTasks || projectCustomTasks.length === 0) {
- return; // No custom tasks defined
- }
- const taskRepository = require("../tasks/taskRepository");
- for (let i = 0; i < projectCustomTasks.length; i++) {
- const taskDef = projectCustomTasks[i];
- if (!taskDef.name) {
- throw new Error(`Missing name for custom task definition of project ${project.metadata.name} ` +
- `at index ${i}`);
- }
- if (taskDef.beforeTask && taskDef.afterTask) {
- throw new Error(`Custom task definition ${taskDef.name} of project ${project.metadata.name} ` +
- `defines both "beforeTask" and "afterTask" parameters. Only one must be defined.`);
- }
- if (this.taskExecutionOrder.length && !taskDef.beforeTask && !taskDef.afterTask) {
- // Iff there are tasks configured, beforeTask or afterTask must be given
- throw new Error(`Custom task definition ${taskDef.name} of project ${project.metadata.name} ` +
- `defines neither a "beforeTask" nor an "afterTask" parameter. One must be defined.`);
- }
-
- let newTaskName = taskDef.name;
- if (this.tasks[newTaskName]) {
- // Task is already known
- // => add a suffix to allow for multiple configurations of the same task
- let suffixCounter = 0;
- while (this.tasks[newTaskName]) {
- suffixCounter++; // Start at 1
- newTaskName = `${taskDef.name}--${suffixCounter}`;
- }
- }
- // Create custom task if not already done (task might be referenced multiple times, first one wins)
- const {specVersion, task} = taskRepository.getTask(taskDef.name);
- const execTask = function() {
- /* Custom Task Interface
- Parameters:
- {Object} parameters Parameters
- {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files
- {module:@ui5/fs.AbstractReader} parameters.dependencies
- Reader or Collection to read dependency files
- {Object} parameters.taskUtil Specification Version dependent interface to a
- [TaskUtil]{@link module:@ui5/builder.tasks.TaskUtil} instance
- {Object} parameters.options Options
- {string} parameters.options.projectName Project name
- {string} [parameters.options.projectNamespace] Project namespace if available
- {string} [parameters.options.configuration] Task configuration if given in ui5.yaml
- Returns:
- {Promise} Promise resolving with undefined once data has been written
- */
- const params = {
- workspace: resourceCollections.workspace,
- dependencies: resourceCollections.dependencies,
- options: {
- projectName: project.metadata.name,
- projectNamespace: project.metadata.namespace,
- configuration: taskDef.configuration
- }
- };
-
- const taskUtilInterface = taskUtil.getInterface(specVersion);
- // Interface is undefined if specVersion does not support taskUtil
- if (taskUtilInterface) {
- params.taskUtil = taskUtilInterface;
- }
- return task(params);
- };
-
- this.tasks[newTaskName] = execTask;
-
- if (this.taskExecutionOrder.length) {
- // There is at least one task configured. Use before- and afterTask to add the custom task
- const refTaskName = taskDef.beforeTask || taskDef.afterTask;
- let refTaskIdx = this.taskExecutionOrder.indexOf(refTaskName);
- if (refTaskIdx === -1) {
- throw new Error(`Could not find task ${refTaskName}, referenced by custom task ${newTaskName}, ` +
- `to be scheduled for project ${project.metadata.name}`);
- }
- if (taskDef.afterTask) {
- // Insert after index of referenced task
- refTaskIdx++;
- }
- this.taskExecutionOrder.splice(refTaskIdx, 0, newTaskName);
- } else {
- // There is no task configured so far. Just add the custom task
- this.taskExecutionOrder.push(newTaskName);
- }
- }
- }
-
- /**
- * Adds a executable task to the builder
- *
- * The order this function is being called defines the build order. FIFO.
- *
- * @param {string} taskName Name of the task which should be in the list availableTasks.
- * @param {Function} taskFunction
- */
- addTask(taskName, taskFunction) {
- if (this.tasks[taskName]) {
- throw new Error(`Failed to add duplicate task ${taskName} for project ${this.project.metadata.name}`);
- }
- if (this.taskExecutionOrder.includes(taskName)) {
- throw new Error(`Builder: Failed to add task ${taskName} for project ${this.project.metadata.name}. ` +
- `It has already been scheduled for execution.`);
- }
- this.tasks[taskName] = taskFunction;
- this.taskExecutionOrder.push(taskName);
- }
-
- /**
- * Check whether a task is defined
- *
- * @private
- * @param {string} taskName
- * @returns {boolean}
- */
- hasTask(taskName) {
- // TODO 3.0: Check whether this method is still required.
- // Only usage within #build seems to be unnecessary as all tasks are also added to the taskExecutionOrder
- return Object.prototype.hasOwnProperty.call(this.tasks, taskName);
- }
-
- /**
- * Takes a list of tasks which should be executed from the available task list of the current builder
- *
- * @param {Array} tasksToRun List of tasks which should be executed
- * @returns {Promise} Returns promise chain with tasks
- */
- async build(tasksToRun) {
- const allTasks = this.taskExecutionOrder.filter((taskName) => {
- // There might be a numeric suffix in case a custom task is configured multiple times.
- // The suffix needs to be removed in order to check against the list of tasks to run.
- //
- // Note: The 'tasksToRun' parameter only allows to specify the custom task name
- // (without suffix), so it executes either all or nothing.
- // It's currently not possible to just execute some occurrences of a custom task.
- // This would require a more robust contract to identify task executions
- // (e.g. via an 'id' that can be assigned to a specific execution in the configuration).
- const taskWithoutSuffixCounter = taskName.replace(/--\d+$/, "");
- return this.hasTask(taskName) && tasksToRun.includes(taskWithoutSuffixCounter);
- });
-
- this.taskLog.addWork(allTasks.length);
-
- for (const taskName of allTasks) {
- const taskFunction = this.tasks[taskName];
- if (typeof taskFunction === "function") {
- await this.executeTask(taskName, taskFunction);
- }
- }
- }
-
- /**
- * Adds progress related functionality to task function.
- *
- * @private
- * @param {string} taskName Name of the task
- * @param {Function} taskFunction Function which executed the task
- * @returns {Promise} Resolves when task has finished
- */
- async executeTask(taskName, taskFunction) {
- this.taskLog.startWork(`Running task ${taskName}...`);
- this._taskStart = performance.now();
- await taskFunction();
- this.taskLog.completeWork(1);
- if (process.env.UI5_LOG_TASK_PERF) {
- this.taskLog.info(`Task succeeded in ${Math.round((performance.now() - this._taskStart))} ms`);
- }
- }
-
- /**
- * Appends the list of 'excludes' to the list of 'patterns'. To harmonize both lists, the 'excludes'
- * are negated and the 'patternPrefix' is added to make them absolute.
- *
- * @private
- * @param {string[]} patterns
- * List of absolute default patterns.
- * @param {string[]} excludes
- * List of relative patterns to be excluded. Excludes with a leading "!" are meant to be re-included.
- * @param {string} patternPrefix
- * Prefix to be added to the excludes to make them absolute. The prefix must have a leading and a
- * trailing "/".
- */
- enhancePatternWithExcludes(patterns, excludes, patternPrefix) {
- excludes.forEach((exclude) => {
- if (exclude.startsWith("!")) {
- patterns.push(`${patternPrefix}${exclude.slice(1)}`);
- } else {
- patterns.push(`!${patternPrefix}${exclude}`);
- }
- });
- }
-}
-
-module.exports = AbstractBuilder;
diff --git a/lib/types/AbstractFormatter.js b/lib/types/AbstractFormatter.js
deleted file mode 100644
index a842cf148..000000000
--- a/lib/types/AbstractFormatter.js
+++ /dev/null
@@ -1,66 +0,0 @@
-const fs = require("graceful-fs");
-
-/**
- * Base class for the formatter implementation of a project type.
- *
- * @abstract
- */
-class AbstractFormatter {
- /**
- * Constructor
- *
- * @param {object} parameters
- * @param {object} parameters.project Project
- */
- constructor({project}) {
- if (new.target === AbstractFormatter) {
- throw new TypeError("Class 'AbstractFormatter' is abstract");
- }
- this._project = project;
- }
-
- /**
- * Formats and validates the project
- *
- * @returns {Promise}
- */
- format() {
- throw new Error("AbstractFormatter: Function format Not implemented");
- }
-
- /**
- * Validates the project
- *
- * @returns {Promise} resolves if successfully validated
- * @throws {Error} if validation fails
- */
- validate() {
- throw new Error("AbstractFormatter: Function validate Not implemented");
- }
-
- /**
- * Checks whether or not the given input is a directory on the file system.
- *
- * @param {string} dirPath directory
- * @returns {Promise} whether or not the given directory exists.
- * true directory exists
- * false directory does not exist
- */
- dirExists(dirPath) {
- return new Promise((resolve, reject) => {
- fs.stat(dirPath, (err, stats) => {
- if (err) {
- if (err.code === "ENOENT") { // "File or directory does not exist"
- resolve(false);
- } else {
- reject(err);
- }
- } else {
- resolve(stats.isDirectory());
- }
- });
- });
- }
-}
-
-module.exports = AbstractFormatter;
diff --git a/lib/types/AbstractUi5Formatter.js b/lib/types/AbstractUi5Formatter.js
deleted file mode 100644
index 252f4e9a7..000000000
--- a/lib/types/AbstractUi5Formatter.js
+++ /dev/null
@@ -1,95 +0,0 @@
-const log = require("@ui5/logger").getLogger("types:AbstractUi5Formatter");
-const path = require("path");
-const fs = require("graceful-fs");
-const AbstractFormatter = require("./AbstractFormatter");
-const {promisify} = require("util");
-const readFile = promisify(fs.readFile);
-
-/**
- * Base class for formatters that require access to some UI5 specific resources
- * like pom.xml
- *
- * @abstract
- */
-class AbstractUi5Formatter extends AbstractFormatter {
- /**
- * Constructor
- *
- * @param {object} parameters
- * @param {object} parameters.project Project
- */
- constructor(parameters) {
- super(parameters);
- if (new.target === AbstractUi5Formatter) {
- throw new TypeError("Class 'AbstractUi5Formatter' is abstract");
- }
- }
-
- /**
- * Checks whether a given string contains a maven placeholder.
- * E.g. ${appId}.
- *
- * @param {string} value String to check
- * @returns {boolean} True if given string contains a maven placeholder
- */
- hasMavenPlaceholder(value) {
- return !!value.match(/^\$\{(.*)\}$/);
- }
-
- /**
- * Resolves a maven placeholder in a given string using the projects pom.xml
- *
- * @param {string} value String containing a maven placeholder
- * @returns {Promise} Resolved string
- */
- async resolveMavenPlaceholder(value) {
- const parts = value && value.match(/^\$\{(.*)\}$/);
- if (parts) {
- log.verbose(`"${value} contains a maven placeholder "${parts[1]}". Resolving from projects pom.xml...`);
- const pom = await this.getPom();
- let mvnValue;
- if (pom.project && pom.project.properties && pom.project.properties[parts[1]]) {
- mvnValue = pom.project.properties[parts[1]];
- } else {
- let obj = pom;
- parts[1].split(".").forEach((part) => {
- obj = obj && obj[part];
- });
- mvnValue = obj;
- }
- if (!mvnValue) {
- throw new Error(`"${value}" couldn't be resolved from maven property ` +
- `"${parts[1]}" of pom.xml of project ${this._project.metadata.name}`);
- }
- return mvnValue;
- } else {
- throw new Error(`"${value}" is not a maven placeholder`);
- }
- }
-
- /**
- * Reads the projects pom.xml file
- *
- * @returns {Promise