Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Oct 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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