@ui5/builder
Version:
UI5 Tooling - Builder
216 lines (194 loc) • 6.56 kB
JavaScript
;
/*
const fs = require("fs");
const path = require("path");
*/
const esprima = require("esprima");
const ComponentAnalyzer = require("../analyzer/ComponentAnalyzer");
const SmartTemplateAnalyzer = require("../analyzer/SmartTemplateAnalyzer");
const FioriElementsAnalyzer = require("../analyzer/FioriElementsAnalyzer");
const XMLCompositeAnalyzer = require("../analyzer/XMLCompositeAnalyzer");
const JSModuleAnalyzer = require("../analyzer/JSModuleAnalyzer");
const XMLTemplateAnalyzer = require("../analyzer/XMLTemplateAnalyzer");
const LibraryFileAnalyzer = require("./LibraryFileAnalyzer");
const ModuleInfo = require("./ModuleInfo");
const ResourceFilterList = require("./ResourceFilterList");
/*
const Resource = require("./Resource");
*/
const log = require("@ui5/logger").getLogger("lbt:resources:ResourcePool");
const jsAnalyzer = new JSModuleAnalyzer();
/*
function scanFileOrDir(fileOrDir, name, pool) {
// console.log("visiting " + fileOrDir + " (" + name + ")");
return new Promise( (resolve, reject) => {
fs.stat(fileOrDir, (statErr, stat) => {
if ( statErr ) {
resolve(statErr);
} else if ( stat.isDirectory() ) {
if ( name && name.slice(-1) !== "/" ) {
name = name + "/";
}
fs.readdir(fileOrDir, (dirErr, files) => {
if ( dirErr ) {
reject(dirErr);
} else {
resolve(
Promise.all(
files.map( (file) => scanFileOrDir(path.join(fileOrDir, file), name + file, pool) )
)
);
}
});
} else if ( /\.(?:js|json|xml|html|properties|library)$/.test(name) ) {
// TODO think about right place for configuration of such a global filtering
const resource = new Resource(pool, name, fileOrDir, stat);
resolve( pool.addResource( resource ) );
} else {
// else: ignore other resource types
resolve(true);
}
});
});
}
*/
async function determineDependencyInfo(resource, rawInfo, pool) {
const info = new ModuleInfo(resource.name);
info.size = resource.fileSize;
if ( /\.js$/.test(resource.name) ) {
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
// 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());
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 ( /(?:^|\/)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();
info.size = xmlView.length;
new XMLTemplateAnalyzer(pool).analyzeView(xmlView, info);
} else if ( /\.control.xml$/.test(resource.name) ) {
const xmlView = await resource.buffer();
info.size = xmlView.length;
new XMLTemplateAnalyzer(pool).analyzeFragment(xmlView, info);
} else if ( /\.fragment.xml$/.test(resource.name) ) {
const xmlView = await resource.buffer();
info.size = xmlView.length;
new XMLTemplateAnalyzer(pool).analyzeFragment(xmlView, info);
}
return info;
}
class ResourcePool {
constructor() {
// this._roots = [];
this._resources = [];
this._resourcesByName = new Map();
this._dependencyInfos = new Map();
this._rawModuleInfos = new Map();
// this.whenReady = Promise.resolve(true);
}
/*
TODO check relevance
addRoot(fileOrFolder, prefix) {
this._roots.push({
root: fileOrFolder,
prefix: prefix
});
log.verbose(" scanning <%s> for resources (prefix='%s')", fileOrFolder, prefix);
const p = scanFileOrDir(fileOrFolder, prefix, this);
this.whenReady = this.whenReady.then( () => p );
}
*/
/**
* Adds a resource to the pool
*
* @param {Resource} resource
* @returns {Promise|undefined} for libraries a Promise is returned undefined otherwise
*/
addResource( resource ) {
if ( this._resourcesByName.has(resource.name) ) {
log.warn("duplicate resource " + resource.name);
// TODO return and let the first one always win?
}
this._resources.push(resource);
this._resourcesByName.set(resource.name, resource);
if ( /\.library$/.test(resource.name) ) {
// read raw-module info from .library files
return resource.buffer().then( (buffer) => {
const infos = LibraryFileAnalyzer.getDependencyInfos( buffer );
for ( const name in infos ) {
if (infos.hasOwnProperty(name)) {
this._rawModuleInfos.set(name, infos[name]);
}
}
});
}
}
async getModuleInfo(name) {
let info = this._dependencyInfos.get(name);
if ( info == null ) {
// console.log("analyzing ", name);
const resource = await this.findResource(name);
info = await determineDependencyInfo( resource, this._rawModuleInfos.get(name), this );
// console.log("finished analyzing ", name);
this._dependencyInfos.set(name, info);
}
return info;
}
async findResource(name) {
const resource = this._resourcesByName.get(name);
if ( resource == null ) {
throw new Error("resource not found in pool: '" + name + "'");
}
return resource;
}
async findResourceWithInfo(name) {
return this.findResource(name).then( (resource) => {
return this.getModuleInfo(name).then( (info) => {
// HACK: attach info to resource
resource.info = info;
return resource;
}, (err) => resource);
});
}
/**
* Finds the resources based matching the given pattern
*
* @param {ResourceFilterList|RegExp} pattern
* @returns {Promise}
*/
async findResources(pattern) {
if ( pattern instanceof ResourceFilterList ) {
return this._resources.filter( (resource) => pattern.matches(resource.name) );
}
return this._resources.filter( (resource) => pattern.test(resource.name) );
}
get size() {
return this._resources.length;
}
get resources() {
return this._resources.slice();
}
}
module.exports = ResourcePool;