Skip to content

Commit

Permalink
[FEATURE] Add new theme-library type (#285)
Browse files Browse the repository at this point in the history
Theme libraries do not require as many features as normal libraries.
For example they do not need a namespace. Also many build tasks are
not required.

Theme libraries must use specVersion 1.1 or higher

Also replace copyright strings in .less and .theme files.

Co-authored-by: Matthias Oßwald <1410947+matz3@users.noreply.github.com>
  • Loading branch information
RandomByte and matz3 committed Jan 10, 2020
1 parent c21e13e commit a59287b
Show file tree
Hide file tree
Showing 12 changed files with 428 additions and 5 deletions.
4 changes: 2 additions & 2 deletions lib/types/library/LibraryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class LibraryBuilder extends AbstractBuilder {
workspace: resourceCollections.workspace,
options: {
copyright: project.metadata.copyright,
pattern: "/resources/**/*.{js,json,library}"
pattern: "/resources/**/*.{js,library,less,theme}"
}
});
});
Expand All @@ -56,7 +56,7 @@ class LibraryBuilder extends AbstractBuilder {
workspace: resourceCollections.workspace,
options: {
version: project.version,
pattern: "/resources/**/*.{js,json,library}"
pattern: "/resources/**/*.{js,json,library,less,theme}"
}
});
});
Expand Down
59 changes: 59 additions & 0 deletions lib/types/themeLibrary/ThemeLibraryBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const AbstractBuilder = require("../AbstractBuilder");
const tasks = { // can't require index.js due to circular dependency
generateComponentPreload: require("../../tasks/bundlers/generateComponentPreload"),
generateFlexChangesBundle: require("../../tasks/bundlers/generateFlexChangesBundle"),
generateBundle: require("../../tasks/bundlers/generateBundle"),
generateLibraryPreload: require("../../tasks/bundlers/generateLibraryPreload"),
generateManifestBundle: require("../../tasks/bundlers/generateManifestBundle"),
generateStandaloneAppBundle: require("../../tasks/bundlers/generateStandaloneAppBundle"),
buildThemes: require("../../tasks/buildThemes"),
createDebugFiles: require("../../tasks/createDebugFiles"),
generateJsdoc: require("../../tasks/jsdoc/generateJsdoc"),
executeJsdocSdkTransformation: require("../../tasks/jsdoc/executeJsdocSdkTransformation"),
generateLibraryManifest: require("../../tasks/generateLibraryManifest"),
generateVersionInfo: require("../../tasks/generateVersionInfo"),
replaceCopyright: require("../../tasks/replaceCopyright"),
replaceVersion: require("../../tasks/replaceVersion"),
uglify: require("../../tasks/uglify")
};

class ThemeLibraryBuilder extends AbstractBuilder {
addStandardTasks({resourceCollections, project, log, buildContext}) {
this.addTask("replaceCopyright", () => {
const replaceCopyright = tasks.replaceCopyright;
return replaceCopyright({
workspace: resourceCollections.workspace,
options: {
copyright: project.metadata.copyright,
pattern: "/resources/**/*.{less,theme}"
}
});
});

this.addTask("replaceVersion", () => {
const replaceVersion = tasks.replaceVersion;
return replaceVersion({
workspace: resourceCollections.workspace,
options: {
version: project.version,
pattern: "/resources/**/*.{less,theme}"
}
});
});

this.addTask("buildThemes", () => {
const buildThemes = tasks.buildThemes;
return buildThemes({
workspace: resourceCollections.workspace,
dependencies: resourceCollections.dependencies,
options: {
projectName: project.metadata.name,
librariesPattern: "/resources/**/*.library",
inputPattern: "/resources/**/themes/*/library.source.less"
}
});
});
}
}

module.exports = ThemeLibraryBuilder;
88 changes: 88 additions & 0 deletions lib/types/themeLibrary/ThemeLibraryFormatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const log = require("@ui5/logger").getLogger("types:themeLibrary:ThemeLibraryFormatter");
const path = require("path");
const AbstractUi5Formatter = require("../AbstractUi5Formatter");


class ThemeLibraryFormatter extends AbstractUi5Formatter {
/**
* Formats and validates the project
*
* @returns {Promise}
*/
async format() {
const project = this._project;
await this.validate();

log.verbose("Formatting theme-library project %s...", project.metadata.name);
project.resources.pathMappings = {
"/resources/": project.resources.configuration.paths.src
};

if (project.resources.configuration.paths.test) {
// Directory 'test' is somewhat optional for theme-libraries
project.resources.pathMappings["/test-resources/"] = project.resources.configuration.paths.test;
} else {
log.verbose(`Ignoring 'test' directory for project ${project.metadata.name}.` +
"Either no setting was provided or the path not found.");
}
}

/**
* Validates the project
*
* @returns {Promise} resolves if successfully validated
* @throws {Error} if validation fails
*/
validate() {
const project = this._project;
return Promise.resolve().then(() => {
if (!project) {
throw new Error("Project is undefined");
} else if (project.specVersion === "0.1" || project.specVersion === "1.0") {
throw new Error(`theme-library type requires "specVersion" 1.1 or higher. Project "specVersion" is: ${project.specVersion}`);
} else if (!project.metadata || !project.metadata.name) {
throw new Error(`"metadata.name" configuration is missing for project ${project.id}`);
} else if (!project.type) {
throw new Error(`"type" configuration is missing for project ${project.id}`);
} else if (project.version === undefined) {
throw new Error(`"version" is missing for project ${project.id}`);
}
if (!project.resources) {
project.resources = {};
}
if (!project.resources.configuration) {
project.resources.configuration = {};
}
if (!project.resources.configuration.paths) {
project.resources.configuration.paths = {};
}
if (!project.resources.configuration.paths.src) {
project.resources.configuration.paths.src = "src";
}
if (!project.resources.configuration.paths.test) {
project.resources.configuration.paths.test = "test";
}

const absoluteSrcPath = path.join(project.path, project.resources.configuration.paths.src);
const absoluteTestPath = path.join(project.path, project.resources.configuration.paths.test);
return Promise.all([
this.dirExists(absoluteSrcPath).then(function(bExists) {
if (!bExists) {
throw new Error(`Could not find source directory of project ${project.id}: ` +
`${absoluteSrcPath}`);
}
}),
this.dirExists(absoluteTestPath).then(function(bExists) {
if (!bExists) {
log.verbose(`Could not find (optional) test directory of project ${project.id}: ` +
`${absoluteSrcPath}`);
// Current signal to following consumers that "test" is not available is null
project.resources.configuration.paths.test = null;
}
})
]);
});
}
}

module.exports = ThemeLibraryFormatter;
15 changes: 15 additions & 0 deletions lib/types/themeLibrary/themeLibraryType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const ThemeLibraryFormatter = require("./ThemeLibraryFormatter");
const ThemeLibraryBuilder = require("./ThemeLibraryBuilder");

module.exports = {
format: function(project) {
return new ThemeLibraryFormatter({project}).format();
},
build: function({resourceCollections, tasks, project, parentLogger, buildContext}) {
return new ThemeLibraryBuilder({resourceCollections, project, parentLogger, buildContext}).build(tasks);
},

// Export type classes for extensibility
Builder: ThemeLibraryBuilder,
Formatter: ThemeLibraryFormatter
};
8 changes: 5 additions & 3 deletions lib/types/typeRepository.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const applicationType = require("./application/applicationType");
const libraryType = require("./library/libraryType");
const themeLibraryType = require("./themeLibrary/themeLibraryType");
const moduleType = require("./module/moduleType");

const types = {
application: applicationType,
library: libraryType,
module: moduleType
"application": applicationType,
"library": libraryType,
"theme-library": themeLibraryType,
"module": moduleType
};

/**
Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/theme.library.e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "theme.library.e",
"version": "1.0.0",
"description": "Simple SAPUI5 based library - test for dev dependencies",
"devDependencies": {
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<theme xmlns="http://www.sap.com/sap.ui.library.xsd" >

<name>my_theme</name>
<vendor>me</vendor>
<copyright>${copyright}</copyright>
<version>${version}</version>

</theme>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"sEntity": "Theme",
"sId": "sap_belize",
"oExtends": "base",
"sVendor": "SAP",
"aBundled": ["sap_belize_plus"],
"mCssScopes": {
"library": {
"sBaseFile": "library",
"sEmbeddingMethod": "APPEND",
"aScopes": [
{
"sLabel": "Contrast",
"sSelector": "sapContrast",
"sEmbeddedFile": "sap_belize_plus.library",
"sEmbeddedCompareFile": "library",
"sThemeIdSuffix": "Contrast",
"sThemability": "PUBLIC",
"aThemabilityFilter": [
"Color"
],
"rExcludeSelector": "\\.sapContrastPlus\\W"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*!
* ${copyright}
*/

* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-touch-callout: none;
-webkit-text-size-adjust: none;
-ms-text-size-adjust: none;
}

.sapUiBody {
width: 100%;
height: 100%;
margin: 0;
font-family: @sapUiFontFamily;
font-size: 1rem;
}
Empty file.
9 changes: 9 additions & 0 deletions test/fixtures/theme.library.e/ui5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
specVersion: "1.1"
type: theme-library
metadata:
name: theme.library.e
copyright: |-
UI development toolkit for HTML5 (OpenUI5)
* (c) Copyright 2009-xxx SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
Loading

0 comments on commit a59287b

Please sign in to comment.