Ich konnte mein Vorhaben realisieren, ich habe BEIDE obigen Ansätze verwendet (
Parsing/Compile+
Preload, und
Promise+
Generator)!
Zunächst parse ich die auflösbaren(*) require calls welche
left-hand assigned sind, also wenn der vorherige Token ein "=" oder ein "return" ist. Die anderen Aufrufe der require Funktion verwenden mutmaßlich eine callbackfunktion als Argument und sind somit in der Laufzeit asynchron und benötigen keine Behandlung.
(*) Mit "Auflösbar" ist gemeint, der identifier/url/modulename ist ein String (ohne vars, etc) und wird NICHT zur Laufzeit geformt.
Mein library ermöglicht zusätzlich asynchrone requires, und require calls in asynchronen Funktionen/zur Laufzeit, im Gegenssatz zu node.js, in node.js werden die imports mit dem file geladen und die requires sollten möglichst am Anfang stehen/synchron sein. Hierzu siehe weiter unten...
Code:
var _javascriptCompiler = function(js, module) {
function p(mod) {
var m = ('undefined' !== typeof mod.parent) ? mod.parent : mod || frdl.main;
return m;
}
function url_rewrite_1(url){
//if('undefined'==typeof require.cache('url'))return url;
//var __U = require.cache('url');
var __U = require('url');
var m = p(module);
var U = __U.parse(m.descriptor.location.url);
var u = U.resolve(url);
var s = (3 === parseInt(m.descriptor.location.server)) ? 5 : m.descriptor.location.server;
u = s + ':' + u;
if (false === strpos(U.path, '.')) u += '.js';
return u;
}
function url_rewrite_2(url){
return url;
}
var _require = ('undefined' !== typeof module && 'undefined' !== typeof module.require) ? module.require : require;
var urlMatchingRegex = /require\(["']([\.]{1,2}\/[^"']*)["'][\)\,]/gi;
js = js.replace(urlMatchingRegex, function(fullMatch, url) {
var u = url_rewrite_1(url);
var fm = frdl.str_replace(url, u, fullMatch);
return fm;
});
var lit = frdl.clone(frdl.lit);
lit.opts.identify_number_literals = true;
lit.opts.identify_simple_literals = false;
lit.opts.overlook_html_comment_markers = false;
var ast = lit.lex(js);
var i = 0, t = false, tok = false, Toks = [];
// var _regex = /([^\n\,;!?\s{}=]+)/i;
var _regex = /([^\n\,;!?\s{}]+)/i;
var requiredUrls = [], reqUrl = false, req = false;
for(i=0; i < ast.length-1; i++){
Toks = [];
tok =ast[i];
if(0===tok.type){
t = tok.val.split(_regex);
frdl.each(t, function(i, t){
if(''!==t && ' ' !== t)Toks.push(t);
});
if('require(' === Toks[Toks.length-1]){
if(2===ast[i+1].type){
reqUrl = ast[i+1].val.substr(1,ast[i+1].val.length-2);
if(')'===ast[i+2].val.substr(0,1) || ','===ast[i+2].val.substr(0,1) ){
req = {
returnTok : Toks[Toks.length-2],
url : reqUrl
};
requiredUrls.push(req);
}
}
}
}
}
function preload_requires(){
frdl.each(requiredUrls, function(i, req){
function loadAsText(){
_require(url_rewrite_2(req.url), function(){
}, require.getCompiler('plain/text'), 'plain/text');
}
if('=' === req.returnTok){
_require(url_rewrite_2(req.url), function(){
});
loadAsText();
}else if('return' === req.returnTok /* && false === !!frdl.main */){
if( 'undefined' === typeof frdl.main){
webfan.$Async(function(){
_require(url_rewrite_2(req.url), function(){
});
},1);
}else{
webfan.$Async(function(){
_require(url_rewrite_2(req.url), function(){
});
},1);
}
loadAsText();
}
});
}
if( false === !!frdl.main){
preload_requires();
}else{
preload_requires();
}
var js_preload_cache = '';
/*
frdl.each(requiredUrls, function(i, req){
if('=' === req.returnTok){
js_preload_cache+="require('"+req.url+"', function(){});";
}else if('return' === req.returnTok){
js_preload_cache+="require('"+req.url+"', function(){});";
}
});
*/
js_preload_cache+="(function(){(function(){return; }());return; }());";
js = lit.generate(ast);
return 'if(\'undefined\'===typeof __filename && module && module.sourceURL){' + 'var __filename = module.sourceURL;'
+ '}' + 'if(\'undefined\'===typeof ___filename){'
+ 'var ___filename = __filename;'
+ '}' + 'if(\'undefined\'===typeof __dirname && __DIR__){'
+ 'var __dirname = __DIR__;' + '}'
+ 'if(\'undefined\'===typeof __FILE__ && __DIR__){' + 'var __FILE__ = __filename;' + '}'
+ '(' + 'function(exports, require, module, frdl, webfan, __filename, __dirname, __FILE__, __DIR__, Widget){'
+ '\r\n'
+ js_preload_cache
+ '\r\n'
+ js
+ '\r\n'
+ 'return exports;' + '\r\n' + '\r\n}'
+ '(exports, require, module, frdl, webfan, __filename, __dirname, __FILE__, __DIR__, (\'undefined\'!==typeof Widget) ? Widget : ((\'undefined\'!==typeof module.Widget) ?module.Widget:undefined)));\r\n//# sourceURL='
+ module.sourceURL;
};
Es verbleiben Fälle, welche nach dem obigen System nicht behandelt werden und immer noch synchron sind.
Hier hat mir folgendes geholfen:
Browser welche synchrone XMLHttpRequests NICHT mehr implementieren (z.B. Edge), unterstützen wahrscheinlich schon GeneratorFunctions ( function *() ) und yield.
Mithilfe dessen sollte es möglich sein eine Art "await" zu simulieren.
Hier gibt es ein recht anschauliches Beispiel:
view-source:https://googlesamples.github.io/web-fundamentals/fundamentals/getting-started/primers/async-generators-example.html
view-source:https://googlesamples.github.io/web-fundamentals/fundamentals/getting-started/primers/utils.js
Meine eigene Implementation ist teilwse noch in Arbeit, es sollen auch Browser unterstützt werden welche noch keine Generator Funktionen oder Arrow Functions bieten, syntax rewrite...
https://github.com/frdl/-Flow/blob/master/hist/mobile.make-require-async.js#L11674
https://github.com/frdl/-Flow/blob/master/hist/mobile.make-require-async.js#L11719
Code:
if( 'function'!==typeof callback
&& 3>opts.recursive_count
){
descriptor.mime = mime;
try{
module.require(module,function (mod){
// alert('Await -ed for mod: '+mod.toString());
}, compiler, mime, opts);
}catch(err){
console.error('error require frdl.Await.spawn: '+err);
}
frdl.Await.spawn.call(descriptor, function $___GENERATOR___(){
"use strict";
try{
var descriptor = this;
var cacheid = cacheID(descriptor.id + (('string' === typeof descriptor.mime) ? '#' + descriptor.mime : ''));
var prom = new Promise(function(resolve, reject) {
(function __check(){
if ('undefined' !== typeof require.cache()[cacheid]) {
resolve(require.cache()[cacheid]);
}else{
webfan.$Async(function(){
__check();
},1);
}
}());
});
}catch(err){
console.error('error require frdl.Await.spawn in Promise: '+err);
}
var waitFor = $___YIELD___(); frdl.Await.waitForPromise(prom);
});
return require.cache()[cacheid];
}