Skip to content

Commit

Permalink
[FIX] Bundling: merge dependency analysis results with raw module inf…
Browse files Browse the repository at this point in the history
…os (#340)

[FIX] Bundling: merge dependency analysis results with raw module infos

Fixes https://github.com/SAP/ui5-builder/issues/338

Also adds tests for Resolver
  • Loading branch information
codeworrior authored Oct 11, 2019
1 parent d60c67d commit af4318a
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 87 deletions.
2 changes: 1 addition & 1 deletion lib/lbt/analyzer/JSModuleAnalyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ class JSModuleAnalyzer {
if ( currentScope.set.size > 0 ) {
info.requiresTopLevelScope = true;
info.exposedGlobals = Array.from(currentScope.set.keys());
// console.log(info.name, info.exposedGlobals);
// console.log(info.name, "exposed globals", info.exposedGlobals, "ignoredGlobals", info.ignoredGlobals);
}

return;
Expand Down
102 changes: 60 additions & 42 deletions lib/lbt/bundle/Resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,21 @@ class BundleResolver {
return match;
}

function isMultiModule(moduleInfo) {
return moduleInfo && moduleInfo.subModules.length > 0 && !/(?:^|\/)library.js$/.test(moduleInfo.name);
function checkForDecomposableBundle(resource) {
if ( resource == null
|| resource.info == null
|| resource.info.subModules.length === 0
|| /(?:^|\/)library.js$/.test(resource.info.name) ) {
return {resource, decomposable: false};
}

return Promise.all(
resource.info.subModules.map((sub) => pool.findResource(sub).catch(() => false))
).then((modules) => {
// it might look more natural to expect 'all' embedded modules to exist in the pool,
// but expecting only 'some' module to exist is a more conservative approach
return ({resource, decomposable: modules.some(($) => ($))});
});
}

function checkAndAddResource(resourceName, depth, msg) {
Expand All @@ -74,50 +87,55 @@ class BundleResolver {
// remember that we have seen this module already
visitedResources[resourceName] = resourceName;

done = pool.findResourceWithInfo(resourceName).then( function(resource) {
const dependencyInfo = resource && resource.info;
let promises = [];

if ( isMultiModule(dependencyInfo) ) {
// multi modules are not added, only their pieces (sub modules)
promises = dependencyInfo.subModules.map( (included) => {
return checkAndAddResource(included, depth + 1,
"**** error: missing submodule " + included + ", included by " + resourceName);
});
} else if ( resource != null ) {
// trace.trace(" checking dependencies of " + resource.name );
selectedResources[resourceName] = resourceName;
selectedResourcesSequence.push(resourceName);

// trace.info(" collecting %s", resource.name);

// add dependencies, if 'resolve' is configured
if ( section.resolve && dependencyInfo ) {
promises = dependencyInfo.dependencies.map( function(required) {
// ignore conditional dependencies if not configured
if ( !section.resolveConditional
&& dependencyInfo.isConditionalDependency(required) ) {
return;
}

return checkAndAddResource( required, depth + 1,
"**** error: missing module " + required + ", required by " + resourceName);
});
done = pool.findResourceWithInfo(resourceName)
.catch( (err) => {
// if the caller provided an error message, log it
if ( msg ) {
log.error(msg);
}
// return undefined
})
.then( (resource) => checkForDecomposableBundle(resource) )
.then( ({resource, decomposable}) => {
const dependencyInfo = resource && resource.info;
let promises = [];

if ( decomposable ) {
// bundles are not added, only their embedded modules
promises = dependencyInfo.subModules.map( (included) => {
return checkAndAddResource(included, depth + 1,
"**** error: missing submodule " + included + ", included by " + resourceName);
});
} else if ( resource != null ) {
// trace.trace(" checking dependencies of " + resource.name );
selectedResources[resourceName] = resourceName;
selectedResourcesSequence.push(resourceName);

// trace.info(" collecting %s", resource.name);

// add dependencies, if 'resolve' is configured
if ( section.resolve && dependencyInfo ) {
promises = dependencyInfo.dependencies.map( function(required) {
// ignore conditional dependencies if not configured
if ( !section.resolveConditional
&& dependencyInfo.isConditionalDependency(required) ) {
return;
}

return checkAndAddResource( required, depth + 1,
"**** error: missing module " + required + ", required by " + resourceName);
});
}

// add renderer, if 'renderer' is configured and if it exists
if ( section.renderer ) {
const rendererModuleName = UI5ClientConstants.getRendererName( resourceName );
promises.push( checkAndAddResource( rendererModuleName, depth + 1) );
// add renderer, if 'renderer' is configured and if it exists
if ( section.renderer ) {
const rendererModuleName = UI5ClientConstants.getRendererName( resourceName );
promises.push( checkAndAddResource( rendererModuleName, depth + 1) );
}
}
}

return Promise.all( promises.filter( ($) => $ ) );
}, function(err) {
if ( msg ) {
log.error(msg);
}
}); // what todo after resource has been visited?
return Promise.all( promises.filter( ($) => $ ) );
});

if ( dependencyTracker != null ) {
dependencyTracker.endVisitDependency(resourceName);
Expand Down
49 changes: 26 additions & 23 deletions lib/lbt/resources/ResourcePool.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,38 @@ async function determineDependencyInfo(resource, rawInfo, pool) {
const info = new ModuleInfo(resource.name);
info.size = resource.fileSize;
if ( /\.js$/.test(resource.name) ) {
// console.log("analyzing %s", resource.file);
const code = await resource.buffer();
info.size = code.length;
const promises = [];
try {
const ast = esprima.parseScript(code.toString(), {comment: true});
jsAnalyzer.analyze(ast, resource.name, info);
new XMLCompositeAnalyzer(pool).analyze(ast, resource.name, info);
} catch (error) {
log.error("failed to parse or analyze %s:", resource.name, error);
}
if ( rawInfo ) {
// modules for which a raw-info was modelled should not be analyzed
// otherwise, we detect the internal dependencies of sap-viz.js, but can't handle them
// as we don't have access to the individual modules

info.rawModule = true;
// console.log("adding preconfigured dependencies for %s:", resource.name, rawInfo.dependencies);
rawInfo.dependencies.forEach( (dep) => info.addDependency(dep) );
} else {
// console.log("analyzing %s", resource.file);
const code = await resource.buffer();
info.size = code.length;
const promises = [];
try {
const ast = esprima.parseScript(code.toString(), {comment: true});
jsAnalyzer.analyze(ast, resource.name, info);
new XMLCompositeAnalyzer(pool).analyze(ast, resource.name, info);
} catch (error) {
log.error("failed to parse or analyze %s:", resource.name, error);
if ( rawInfo.requiresTopLevelScope ) {
info.requiresTopLevelScope = true;
}
if ( /(?:^|\/)Component\.js/.test(resource.name) ) {
promises.push(
new ComponentAnalyzer(pool).analyze(resource, info),
new SmartTemplateAnalyzer(pool).analyze(resource, info),
new FioriElementsAnalyzer(pool).analyze(resource, info)
);
if ( rawInfo.ignoredGlobals ) {
info.ignoredGlobals = rawInfo.ignoredGlobals;
}

await Promise.all(promises);
}
if ( /(?:^|\/)Component\.js/.test(resource.name) ) {
promises.push(
new ComponentAnalyzer(pool).analyze(resource, info),
new SmartTemplateAnalyzer(pool).analyze(resource, info),
new FioriElementsAnalyzer(pool).analyze(resource, info)
);
}

await Promise.all(promises);

// console.log(info);
} else if ( /\.view.xml$/.test(resource.name) ) {
const xmlView = await resource.buffer();
Expand Down
47 changes: 35 additions & 12 deletions lib/tasks/bundlers/generateLibraryPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,52 @@ function getBundleDefinition(namespace) {
name: `${namespace}/library-preload.js`,
defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"],
sections: [
{
// exclude the content of sap-ui-core by declaring it as 'provided'
mode: "provided",
filters: [
"ui5loader-autoconfig.js",
"sap/ui/core/Core.js"
],
resolve: true
},
{
mode: "preload",
filters: [
`${namespace}/`,
`!${namespace}/.library`,
`!${namespace}/designtime/`,
`!${namespace}/**/*.designtime.js`,
`!${namespace}/**/*.support.js`,
`!${namespace}/themes/`,
`!${namespace}/cldr/`,
`!${namespace}/messagebundle*`,

"*.js",
"sap/base/",
"sap/ui/base/",
"sap/ui/xml/",
"sap/ui/dom/",
"sap/ui/events/",
"sap/ui/model/",
"sap/ui/security/",
"sap/ui/util/",
"sap/ui/Global.js",

// files are already part of sap-ui-core.js
"!sap/ui/thirdparty/baseuri.js",
"!sap/ui/thirdparty/es6-promise.js",
"!sap/ui/thirdparty/es6-string-methods.js",
"!sap/ui/thirdparty/mdn-object-assign.js",
"!jquery.sap.global.js",
"!ui5loader-autoconfig.js",
"!ui5loader.js",
"!ui5loader-amd.js",
"!sap-ui-*.js"
// include only thirdparty that is very likely to be used
"sap/ui/thirdparty/crossroads.js",
"sap/ui/thirdparty/caja-htmlsanitizer.js",
"sap/ui/thirdparty/hasher.js",
"sap/ui/thirdparty/signals.js",
"sap/ui/thirdparty/jquery-mobile-custom.js",
"sap/ui/thirdparty/jqueryui/jquery-ui-core.js",
"sap/ui/thirdparty/jqueryui/jquery-ui-position.js",

// other excludes (not required for productive scenarios)
"!sap-ui-*.js",
"!sap/ui/core/support/",
"!sap/ui/core/plugin/DeclarativeSupport.js",
"!sap/ui/core/plugin/LessSupport.js"

],
resolve: false,
resolveConditional: false,
Expand All @@ -56,6 +72,9 @@ function getBundleDefinition(namespace) {
filters: [
`${namespace}/`,
`!${namespace}/.library`,
`!${namespace}/designtime/`,
`!${namespace}/**/*.designtime.js`,
`!${namespace}/**/*.support.js`,
`!${namespace}/themes/`,
`!${namespace}/messagebundle*`
],
Expand Down Expand Up @@ -232,7 +251,11 @@ module.exports = function({workspace, dependencies, options}) {
const libraryNamespace = libraryNamespaceMatch[1];
return moduleBundler({
options: {
bundleDefinition: getBundleDefinition(libraryNamespace)
bundleDefinition: getBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: true,
usePredefineCalls: true
}
},
resources
}).then(([bundle]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
jQuery.sap.registerPreloadedModules({
"version":"2.0",
"modules":{
"sap/ui/core/Core.js":function(){
},
"sap/ui/core/some.js":function(){/*!
* ${copyright}
*/
Expand Down
Loading

0 comments on commit af4318a

Please sign in to comment.