gulp-ui5-eager-preload
Version:
[](https://www.npmjs.com/package/gulp-ui5-eager-preload)
295 lines (263 loc) • 7.32 kB
JavaScript
var { reduce, forEach, isEmpty } = require("lodash");
var { dirname, join: pathJoin } = require("path");
var { warn } = require("console");
var fetch = require("node-fetch");
var UglifyJS = require("uglify-js");
var parseString = require('xml2js').parseString;
var crypto = require("crypto");
var { UI5Cache } = require("./cache");
var { eachDeep } = require('deepdash')(require('lodash'));
var persistCache = UI5Cache.Load();
var FIVE_MINUTES = 5 * 60 * 1000;
var BASE64 = "base64";
/**
* md5 hash
*/
var md5 = s => {
var md5 = crypto.createHash("md5");
return md5.update(s).digest("hex");
};
var readBinary = async url => {
var GlobalResourceCache = persistCache.get("GlobalResourceCache") || {};
var hash = md5(url);
var base64Content = GlobalResourceCache[hash];
if (!base64Content) {
var response = await fetch(url, { timeout: FIVE_MINUTES });
var buf = await response.buffer();
GlobalResourceCache[hash] = buf.toString(BASE64);
persistCache.set("GlobalResourceCache", GlobalResourceCache);
return buf;
} else {
return Buffer.from(base64Content, BASE64);
}
};
var readURLFromCache = async url => {
var GlobalResourceCache = persistCache.get("GlobalResourceCache") || {};
var hash = md5(url);
var urlContent = GlobalResourceCache[hash];
if (!urlContent) {
var response = await fetch(url, { timeout: FIVE_MINUTES });
urlContent = await response.text();
GlobalResourceCache[hash] = urlContent;
persistCache.set("GlobalResourceCache", GlobalResourceCache);
}
return urlContent;
};
var fetchSource = async(mName, resourceRoot = "") => {
var url = `${resourceRoot}${mName}.js`;
try {
return await readURLFromCache(url);
} catch (error) {
warn(`fetch ${mName} failed ${error}`);
throw error;
}
};
var fetchAllResource = async(resourceList = [], resourceRoot = "") => {
var rt = {};
await Promise.all(
resourceList.map(async r => {
var url = `${resourceRoot}${r}`;
try {
rt[r] = await readURLFromCache(url);
return rt[r];
} catch (error) {
warn(`fetch ${r} failed ${error}`);
throw error;
}
})
);
persistCache.PersistAsync();
return rt;
};
/**
* find modules in sap.ui.define parttern
*/
var findAllUi5StandardModules = (source, sourceName) => {
var base = dirname(sourceName);
var groups = /sap\.ui\.define\(.*?(\[.*?\])/g.exec(source);
if (groups && groups.length > 0) {
var sArray = groups[1].replace(/'/g, '"');
const dependencies = JSON.parse(sArray);
return dependencies.map(d => {
if (d.startsWith("./") || d.startsWith("../")) {
d = pathJoin(base, d);
// replace \ to / after join
d = d.replace(/\\/g, "/");
}
return d;
});
}
return [];
};
var findAllUi5ViewModules = async(source, sourceName) => {
try {
return await new Promise((resolve, reject) => {
var ds = new Set();
parseString(source, { xmlns: true }, function(err, result) {
if (err) {
reject(err);
} else {
eachDeep(result, (value) => {
if (value && value.$ns) {
var mName = `${value.$ns.uri}.${value.$ns.local}`.replace(/\./g, "/");
ds.add(mName);
}
});
resolve(Array.from(ds));
}
});
});
} catch (error) {
warn(`parse ${sourceName} modules failed: ${error}`);
return [];
}
};
var findAllImportModules = (source, sourceName = "") => {
var base = dirname(sourceName);
var rt = [];
var matchedTexts = source.match(/import.*?["|'](.*?)["|']/g);
if (matchedTexts) {
rt = matchedTexts.map(t => {
var importName = /import.*?["|'](.*?)["|']/g.exec(t)[1];
if (importName.startsWith("./")) {
importName = pathJoin(base, importName).replace(/\\/g, "/");
}
return importName;
});
}
return rt;
};
// change recursively to iteration
var resolveUI5Module = async(sModuleNames = [], resourceRoot) => {
var globalModuleCache = persistCache.get("GlobalModuleCache") || {};
// this time used modules
var modules = {};
var moduleDeps = persistCache.get("moduleDeps") || {};
// set entry
moduleDeps["entry"] = sModuleNames;
for (; ;) {
var needToBeLoad = new Set();
forEach(moduleDeps, dep => {
forEach(dep, d => {
if (modules[d] == undefined) {
needToBeLoad.add(d);
}
});
});
if (isEmpty(needToBeLoad)) {
// no more dependencies need to be analyzed
// break from this loop
break;
} else {
await Promise.all(
Array.from(needToBeLoad).map(async mName => {
try {
var source = await fetchSource(mName, resourceRoot);
// use cache here
modules[mName] = source;
if (!moduleDeps[mName]) {
moduleDeps[mName] = findAllUi5StandardModules(source, mName);
}
} catch (error) {
modules[mName] = "";
moduleDeps[mName] = [];
}
})
);
}
}
persistCache.set("GlobalModuleCache", Object.assign(globalModuleCache, modules));
persistCache.set("moduleDeps", (Object.assign(persistCache.get("moduleDeps") || {}), moduleDeps));
return modules;
};
/**
* UI5 Library List
*/
var UI5Libraries = [
"sap/ui/core",
"sap/ui/layout",
"sap/ui/unified",
"sap/ui/table",
"sap/ui/viz",
"sap/ui/suite",
"sap/ui/richtexteditor",
"sap/ui/comp",
"sap/m",
"sap/f",
"sap/suite/ui",
"sap/gantt",
"sap/ushell",
"sap/tnt",
"sap/uxap"
];
/**
* find out all ui5 libraries
* @param {string[]} modules name
*
* @returns {string[]} lib names
*/
var findAllLibraries = (modules = []) => {
var rt = new Set();
forEach(modules, m => {
forEach(UI5Libraries, l => {
if (m.startsWith(l)) {
rt.add(l);
}
});
});
return Array.from(rt);
};
var isUI5StandardModule = sModuleName => {
var rt = false;
UI5Libraries.forEach(packageName => {
if (sModuleName && sModuleName.startsWith(packageName)) {
rt = true;
}
});
return rt;
};
/**
* temporary in memory uglify cache
*/
var TmpUglifyNameCache = {};
/**
* To generate preload file content
* @param {*} cache object
* @param {*} resources list
*/
var generatePreloadFile = (cache = {}, resources = {}) => {
var modules = reduce(
cache,
(pre, moduleSource, moduleName) => {
// ignore core modules, will be load on bootstrap
if (!moduleName.startsWith("sap/ui/core")) {
var sourceHash = md5(moduleSource);
var compressed = TmpUglifyNameCache[sourceHash];
if (!compressed) {
compressed = UglifyJS.minify(moduleSource).code;
}
pre[`${moduleName}.js`] = compressed;
TmpUglifyNameCache[sourceHash] = compressed;
}
return pre;
}, {}
);
forEach(resources, (content, resourceName) => {
modules[resourceName] = content;
});
return `sap.ui.require.preload(${JSON.stringify(modules)})`;
};
module.exports = {
fetchAllResource,
generatePreloadFile,
fetchSource,
findAllUi5ViewModules,
isUI5StandardModule,
findAllImportModules,
findAllUi5StandardModules,
resolveUI5Module,
findAllLibraries,
readURLFromCache,
readBinary,
persistCache
};