Skip to content

Commit

Permalink
[FEATURE] Generate the AppCacheBuster index file for apps
Browse files Browse the repository at this point in the history
  • Loading branch information
petermuessig committed Apr 12, 2019
1 parent b39b944 commit dd653c8
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 2 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = {
generateManifestBundle: require("./lib/tasks/bundlers/generateManifestBundle"),
generateStandaloneAppBundle: require("./lib/tasks/bundlers/generateStandaloneAppBundle"),
generateBundle: require("./lib/tasks/bundlers/generateBundle"),
generateCachebusterInfo: require("./lib/tasks/generateCachebusterInfo"),
buildThemes: require("./lib/tasks/buildThemes"),
createDebugFiles: require("./lib/tasks/createDebugFiles"),
executeJsdocSdkTransformation: require("./lib/tasks/jsdoc/executeJsdocSdkTransformation"),
Expand Down
1 change: 1 addition & 0 deletions lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function composeTaskList({dev, selfContained, jsdoc, includedTasks, excludedTask
selectedTasks.transformBootstrapHtml = false;
selectedTasks.generateJsdoc = false;
selectedTasks.executeJsdocSdkTransformation = false;
selectedTasks.generateCachebusterInfo = false;
selectedTasks.generateApiIndex = false;

if (selfContained) {
Expand Down
30 changes: 30 additions & 0 deletions lib/tasks/generateCachebusterInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const resourceFactory = require("@ui5/fs").resourceFactory;

/**
* Task to generate the application cachebuster info file.
*
* @public
* @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
* @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
*/
module.exports = function({workspace, dependencies, options}) {
return workspace.byGlob(`/resources/${options.namespace}/**/*`)
.then(async (resources) => {
const cachebusterInfo = {};
const regex = new RegExp(`^/resources/${options.namespace}/`);
resources.forEach((resource) => {
const normalizedPath = resource.getPath().replace(regex, "");
cachebusterInfo[normalizedPath] = resource.getStatInfo().mtime.getTime();
});
const cachebusterInfoResource = resourceFactory.createResource({
path: `/resources/${options.namespace}/sap-ui-cachebuster-info.json`,
string: JSON.stringify(cachebusterInfo, null, 2)
});
return workspace.write(cachebusterInfoResource);
});
};
3 changes: 2 additions & 1 deletion lib/tasks/taskRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const tasks = {
generateComponentPreload: require("./bundlers/generateComponentPreload"),
generateStandaloneAppBundle: require("./bundlers/generateStandaloneAppBundle"),
generateBundle: require("./bundlers/generateBundle"),
generateLibraryPreload: require("./bundlers/generateLibraryPreload")
generateLibraryPreload: require("./bundlers/generateLibraryPreload"),
generateCachebusterInfo: require("./generateCachebusterInfo")
};

function getTask(taskName) {
Expand Down
13 changes: 13 additions & 0 deletions lib/types/application/ApplicationBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const tasks = { // can't require index.js due to circular dependency
generateManifestBundle: require("../../tasks/bundlers/generateManifestBundle"),
generateStandaloneAppBundle: require("../../tasks/bundlers/generateStandaloneAppBundle"),
generateBundle: require("../../tasks/bundlers/generateBundle"),
generateCachebusterInfo: require("../../tasks/generateCachebusterInfo"),
buildThemes: require("../../tasks/buildThemes"),
createDebugFiles: require("../../tasks/createDebugFiles"),
generateVersionInfo: require("../../tasks/generateVersionInfo"),
Expand Down Expand Up @@ -161,6 +162,18 @@ class ApplicationBuilder extends AbstractBuilder {
});
});

if (project.metadata.namespace) {
this.addTask("generateCachebusterInfo", () => {
return tasks.generateCachebusterInfo({
workspace: resourceCollections.workspace,
dependencies: resourceCollections.dependencies,
options: {
namespace: project.metadata.namespace
}
});
});
}

this.addTask("generateApiIndex", () => {
return tasks.generateApiIndex({
workspace: resourceCollections.workspace,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sap.ui.define(["sap/ui/core/UIComponent"], function(UIComponent){
"use strict";
return UIComponent.extend('application.g.Component', {
metadata: {
manifest: "json"
}
});
});
13 changes: 13 additions & 0 deletions test/expected/build/application.g/cachebuster/Component-preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
jQuery.sap.registerPreloadedModules({
"version":"2.0",
"modules":{
"application.g/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.Component",{metadata:{manifest:"json"}})});
},
"application.g/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g","type":"application","applicationVersion":{"version":"1.0.0"},"embeds":["embedded"],"title":"{{title}}"},"customCopyrightString":"Some fancy copyright"}',
"application.g/subcomponentA/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.subcomponentA.Component",{metadata:{manifest:"json"}})});
},
"application.g/subcomponentA/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentA","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}',
"application.g/subcomponentB/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.subcomponentB.Component",{metadata:{manifest:"json"}})});
},
"application.g/subcomponentB/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentB","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}'
}});
1 change: 1 addition & 0 deletions test/expected/build/application.g/cachebuster/Component.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions test/expected/build/application.g/cachebuster/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "application.g",
"type": "application",
"applicationVersion": {
"version": "1.0.0"
},
"embeds": ["embedded"],
"title": "{{title}}"
},
"customCopyrightString": "Some fancy copyright"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Component-dbg.js": 1540570258000,
"subcomponentA/manifest.json": 1540570258000,
"subcomponentB/manifest.json": 1540570258000,
"subcomponentB/Component.js": 1540570258000,
"subcomponentA/Component.js": 1540570258000,
"Component.js": 1540570258000,
"Component-preload.js": 1554823987259,
"subcomponentB/Component-dbg.js": 1540570258000,
"subcomponentA/Component-dbg.js": 1540570258000,
"manifest.json": 1540570258000
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sap.ui.define(["sap/ui/core/UIComponent"], function(UIComponent){
"use strict";
return UIComponent.extend('application.g.subcomponentA.Component', {
metadata: {
manifest: "json"
}
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "application.g.subcomponentA",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
},
"embeds": ["embedded"],
"title": "{{title}}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sap.ui.define(["sap/ui/core/UIComponent"], function(UIComponent){
"use strict";
return UIComponent.extend('application.g.subcomponentB.Component', {
metadata: {
manifest: "json"
}
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "application.g.subcomponentB",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
},
"embeds": ["embedded"],
"title": "{{title}}"
}
}
8 changes: 7 additions & 1 deletion test/lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ async function checkFileContentsIgnoreLineFeeds(expectedFiles, expectedPath, des
const currentFileContentPromise = readFile(destFile, "utf8");
const expectedFileContentPromise = readFile(expectedFile, "utf8");
const assertContents = ([currentContent, expectedContent]) => {
assert.equal(currentContent.replace(newLineRegexp, "\n"), expectedContent.replace(newLineRegexp, "\n"));
if (expectedFile.endsWith("sap-ui-cachebuster-info.json")) {
currentContent = JSON.parse(currentContent.replace(/(:\s+)(\d+)/g, ": 0"));
expectedContent = JSON.parse(expectedContent.replace(/(:\s+)(\d+)/g, ": 0"));
assert.deepEqual(currentContent, expectedContent);
} else {
assert.equal(currentContent.replace(newLineRegexp, "\n"), expectedContent.replace(newLineRegexp, "\n"));
}
};
await Promise.all([currentFileContentPromise, expectedFileContentPromise]).then(assertContents);
}
Expand Down
84 changes: 84 additions & 0 deletions test/lib/tasks/generateCachebusterInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const {test} = require("ava");
const fs = require("fs");
const path = require("path");
const chai = require("chai");
chai.use(require("chai-fs"));
const assert = chai.assert;


const ui5Builder = require("../../../");
const builder = ui5Builder.builder;
const applicationGPath = path.join(__dirname, "..", "..", "fixtures", "application.g");

const recursive = require("recursive-readdir");

const findFiles = (folder) => {
return new Promise((resolve, reject) => {
recursive(folder, (err, files) => {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
};

test("integration: Build application.g with manifestBundler", (t) => {
const destPath = path.join("test", "tmp", "build", "application.g", "cachebuster");
const expectedPath = path.join("test", "expected", "build", "application.g", "cachebuster");
const excludedTasks = ["generateVersionInfo"];
const includedTasks = ["generateCachebusterInfo"];

return builder.build({
tree: applicationGTree,
destPath,
excludedTasks,
includedTasks
}).then(() => {
return findFiles(expectedPath);
}).then((expectedFiles) => {
// Check for all directories and files
assert.directoryDeepEqual(destPath, expectedPath);

// Check for all file contents
expectedFiles.forEach((expectedFile) => {
const relativeFile = path.relative(expectedPath, expectedFile);
const destFile = path.join(destPath, relativeFile);
if (expectedFile.endsWith("sap-ui-cachebuster-info.json")) {
const currentContent = JSON.parse(fs.readFileSync(destFile, "utf-8").replace(/(:\s+)(\d+)/g, ": 0"));
const expectedContent = JSON.parse(fs.readFileSync(expectedFile, "utf-8").replace(/(:\s+)(\d+)/g, ": 0"));
assert.deepEqual(currentContent, expectedContent);
} else {
assert.fileEqual(destFile, expectedFile);
}
});
t.pass();
});
});

const applicationGTree = {
"id": "application.g",
"version": "1.0.0",
"path": applicationGPath,
"dependencies": [],
"builder": {},
"_level": 0,
"specVersion": "0.1",
"type": "application",
"metadata": {
"name": "application.g",
"namespace": "application.g",
"copyright": "Some fancy copyright"
},
"resources": {
"configuration": {
"paths": {
"webapp": "webapp"
}
},
"pathMappings": {
"/": "webapp"
}
}
};
3 changes: 3 additions & 0 deletions test/lib/types/application/ApplicationBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ test("Instantiation", (t) => {
"createDebugFiles",
"uglify",
"generateVersionInfo",
"generateCachebusterInfo",
"generateApiIndex"
], "ApplicationBuilder is instantiated with standard tasks");
});
Expand All @@ -89,6 +90,7 @@ test("Instantiation without component preload project configuration", (t) => {
"createDebugFiles",
"uglify",
"generateVersionInfo",
"generateCachebusterInfo",
"generateApiIndex"
], "ApplicationBuilder is still instantiated with standard tasks");
});
Expand Down Expand Up @@ -136,6 +138,7 @@ test("Instantiation with custom tasks", (t) => {
"uglify",
"replaceVersion--1",
"generateVersionInfo",
"generateCachebusterInfo",
"generateApiIndex"
], "ApplicationBuilder is still instantiated with standard tasks");
});

0 comments on commit dd653c8

Please sign in to comment.