Skip to content

Commit

Permalink
[FEATURE] Add generateResourcesJson task (#390)
Browse files Browse the repository at this point in the history
Introduces new task "generateResourcesJson" which creates the file resources.json for libraries, applications and themes.
The task analyses the resources and collects meta information and stores them in a file called resources.json.
This meta information includes module name, size, dependencies, includes, ...

Co-authored-by: Tobias Sorn <tobias.sorn@sap.com>
Co-authored-by: Matthias Osswald <mat.osswald@sap.com>
  • Loading branch information
3 people authored Aug 24, 2020
1 parent 4ca59a7 commit 021f439
Show file tree
Hide file tree
Showing 79 changed files with 2,942 additions and 88 deletions.
5 changes: 5 additions & 0 deletions lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ function composeTaskList({dev, selfContained, jsdoc, includedTasks, excludedTask
selectedTasks.generateCachebusterInfo = false;
selectedTasks.generateApiIndex = 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;
Expand Down
64 changes: 60 additions & 4 deletions lib/lbt/analyzer/JSModuleAnalyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ function getDocumentation(node) {
* @private
*/
class JSModuleAnalyzer {
/**
* Analyzes the JS AST
*
* @param {object} ast js ast
* @param {string} defaultName default name
* @param {ModuleInfo} info module info
*/
analyze(ast, defaultName, info) {
let mainModuleFound = false;
/**
Expand All @@ -260,6 +267,20 @@ class JSModuleAnalyzer {
*/
let nModuleDeclarations = 0;

/**
* Whether or not this is a UI5 module
*
* When in the non-conditional module execution there is a call to:
* <ul>
* <li>sap.ui.define call</li>
* <li>jQuery.sap.declare call</li>
* </ul>
* this value is true
*
* @type {boolean}
*/
let bIsUi5Module = false;

// first analyze the whole AST...
visit(ast, false);

Expand Down Expand Up @@ -307,7 +328,7 @@ class JSModuleAnalyzer {
info.addImplicitDependency(UI5ClientConstants.MODULE__UI5LOADER_AUTOCONFIG);
}

if ( nModuleDeclarations === 0 && info.dependencies.length === 0 && info.subModules.length === 0 ) {
if ( !bIsUi5Module ) {
// when there are no indicators for module APIs, mark the module as 'raw' module
info.rawModule = true;
}
Expand All @@ -320,7 +341,6 @@ class JSModuleAnalyzer {
// console.log(info.name, "exposed globals", info.exposedGlobals, "ignoredGlobals", info.ignoredGlobals);
}

return;

// hoisted functions
function setMainModuleInfo(name, description) {
Expand Down Expand Up @@ -353,6 +373,7 @@ class JSModuleAnalyzer {
// recognized a call to jQuery.sap.declare()
nModuleDeclarations++;
info.setFormat(ModuleFormat.UI5_LEGACY);
bIsUi5Module = true;
onDeclare(node);
} else if ( !conditional &&
(isMethodCall(node, CALL_SAP_UI_DEFINE) || isMethodCall(node, CALL_AMD_DEFINE)) ) {
Expand All @@ -364,6 +385,7 @@ class JSModuleAnalyzer {
} else {
info.setFormat(ModuleFormat.AMD);
}
bIsUi5Module = true;
onDefine(node);

const args = node.arguments;
Expand All @@ -380,8 +402,24 @@ class JSModuleAnalyzer {
}
} else if ( isMethodCall(node, CALL_REQUIRE_PREDEFINE) || isMethodCall(node, CALL_SAP_UI_PREDEFINE) ) {
// recognized a call to require.predefine() or sap.ui.predefine()
if (!conditional) {
bIsUi5Module = true;
}
info.setFormat(ModuleFormat.UI5_DEFINE);
onSapUiPredefine(node);
onSapUiPredefine(node, conditional);

const args = node.arguments;
let iArg = 0;
if ( iArg < args.length && isString(args[iArg]) ) {
iArg++;
}
if ( iArg < args.length && args[iArg].type == Syntax.ArrayExpression ) {
iArg++;
}
if ( iArg < args.length && isCallableExpression(args[iArg]) ) {
// unconditionally execute the factory function
visit(args[iArg].body, conditional);
}
} else if ( isMethodCall(node, CALL_SAP_UI_REQUIRE) || isMethodCall(node, CALL_AMD_REQUIRE) ) {
// recognized a call to require() or sap.ui.require()
if ( isMethodCall(node, CALL_SAP_UI_REQUIRE) ) {
Expand All @@ -404,17 +442,24 @@ class JSModuleAnalyzer {
} else if ( isMethodCall(node, CALL_REQUIRE_SYNC) || isMethodCall(node, CALL_SAP_UI_REQUIRE_SYNC) ) {
// recognizes a call to sap.ui.requireSync
info.setFormat(ModuleFormat.UI5_DEFINE);

onSapUiRequireSync(node, conditional);
} else if ( isMethodCall(node, CALL_JQUERY_SAP_REQUIRE) ) {
// recognizes a call to jQuery.sap.require
info.setFormat(ModuleFormat.UI5_LEGACY);
onJQuerySapRequire(node, conditional);
} else if ( isMethodCall(node, CALL_JQUERY_SAP_REGISTER_PRELOADED_MODULES) ) {
// recognizes a call to jQuery.sap.registerPreloadedModules
if (!conditional) {
bIsUi5Module = true;
}
info.setFormat(ModuleFormat.UI5_LEGACY);
onRegisterPreloadedModules(node, /* evoSyntax= */ false);
} else if ( isMethodCall(node, CALL_SAP_UI_REQUIRE_PRELOAD) ) {
// recognizes a call to sap.ui.require.preload
if (!conditional) {
bIsUi5Module = true;
}
info.setFormat(ModuleFormat.UI5_DEFINE);
onRegisterPreloadedModules(node, /* evoSyntax= */ true);
} else if ( isCallableExpression(node.callee) ) {
Expand Down Expand Up @@ -560,6 +605,7 @@ class JSModuleAnalyzer {

if ( i < nArgs ) {
if ( isString(args[i]) ) {
// sap.ui.requireSync does not support relative dependencies
const moduleName = ModuleName.fromRequireJSName( args[i].value );
info.addDependency(moduleName, conditional);
} else {
Expand All @@ -569,7 +615,7 @@ class JSModuleAnalyzer {
}
}

function onSapUiPredefine(predefineCall) {
function onSapUiPredefine(predefineCall, conditional) {
const args = predefineCall.arguments;
const nArgs = args.length;
let i = 0;
Expand All @@ -578,6 +624,16 @@ class JSModuleAnalyzer {
if ( i < nArgs && isString(args[i]) ) {
const moduleName = ModuleName.fromRequireJSName( args[i++].value );
info.addSubModule(moduleName);

// add dependencies
// to correctly identify dependencies e.g. of a library-preload
const elementArg = args[i++];
if (elementArg && elementArg.type === Syntax.ArrayExpression) {
elementArg.elements.forEach((element) => {
const dependencyName = ModuleName.resolveRelativeRequireJSName(moduleName, element.value);
info.addDependency(dependencyName, conditional);
});
}
} else {
log.warn("sap.ui.predefine call is missing a module name (ignored)");
}
Expand Down
2 changes: 1 addition & 1 deletion lib/lbt/bundle/Builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const {SectionType} = require("./BundleDefinition");
const BundleWriter = require("./BundleWriter");
const log = require("@ui5/logger").getLogger("lbt:bundle:Builder");

const copyrightCommentsPattern = /copyright|\(c\)(?:[0-9]+|\s+[0-9A-za-z])|released under|license|\u00a9/i;
const copyrightCommentsPattern = /copyright|\(c\)(?:[0-9]+|\s+[0-9A-za-z])|released under|license|\u00a9|^@ui5-bundle-raw-include |^@ui5-bundle /i;
const xmlHtmlPrePattern = /<(?:\w+:)?pre>/;

const strReplacements = {
Expand Down
24 changes: 24 additions & 0 deletions lib/lbt/resources/LocatorResource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const Resource = require("./Resource");


function extractName(path) {
return path.slice( "/resources/".length);
}


class LocatorResource extends Resource {
constructor(pool, resource) {
super(pool, extractName(resource.getPath()), null, resource.getStatInfo());
this.resource = resource;
}

buffer() {
return this.resource.getBuffer();
}

getProject() {
return this.resource._project;
}
}

module.exports = LocatorResource;
20 changes: 20 additions & 0 deletions lib/lbt/resources/LocatorResourcePool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const ResourcePool = require("./ResourcePool");
const LocatorResource = require("./LocatorResource");


class LocatorResourcePool extends ResourcePool {
constructor() {
super();
}

prepare(resources) {
resources = resources.filter( (res) => !res.getStatInfo().isDirectory() );
return Promise.all(
resources.map(
(resource) => this.addResource( new LocatorResource(this, resource) )
).filter(Boolean)
);
}
}

module.exports = LocatorResourcePool;
6 changes: 6 additions & 0 deletions lib/lbt/resources/ModuleInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class ModuleInfo {
/**
* 'raw' modules are modules that don't use UI5's module system (require/declare)
* TODO align with module format (ui5, amd, es6, raw)
*
* A raw module is a module which does not have in its non-conditional execution:
* <ul>
* <li>sap.ui.define call</li>
* <li>jQuery.sap.declare call</li>
* </ul>
*/
this.rawModule = false;

Expand Down
Loading

0 comments on commit 021f439

Please sign in to comment.