bpstrngr / interface

Ecmascript module interface for clients/imports/functions.
0 stars 1 forks source link

Standardize synchronous circular requires #6

Open bpstrngr opened 12 months ago

bpstrngr commented 12 months ago

Synchronous circular requires (commonjs equivalent of static default imports) are individually edited in some sources (CSSOM, JSDOM), but a generic pattern can be recognized.

The edits are always a delayed reassignment from dynamic import (const a=require("a") -> let a;setTimeout(async function expect_hoisted_a() {import("a").then(({default:module})=>a=module)},3000) - note that dynamic import is only supported for requires in already async scopes; see issue #9 ), supported by an equal timeout before the default export of module.exports in the entry module since the dynamic imports become promise resolutions of hoisted variables of co-dependent modules stacked lower in the module scope when bundled:

// entry module: 
await new Promise(resolve=>setTimeout(resolve,3000));
export default module.exports;

// dependent module: 
var codependent;
setTimeout(async function expect_hoisted_dependency()
{import("codependent").then(({default:module})=>codependent=module)
},3000);
export default module.exports;

// codependent module: 
import dependent from "dependent";
module.exports={dependent}
export default module.exports;

// bundle: 
var codependent;
setTimeout(async function expect_hoisted_a()
{Promise.resolve(dependency).then(({default:module})=>codependent=module)
},3000);
var dependent={default};
var dependency={default:dependent};
await new Promise(resolve=>setTimeout(resolve,3000));
export default module.exports;

Collecting and awaiting the dynamic import reassignments instead of the arbitrary delay at the end of bundle, as well as gaining access to rollup's hoisted variable name to recursively ascertain the local reassignments, or potential modifications to the module hierarchy could be further improvements to this generalization.

An illustrative subject circular dependency graph (CSSOM): image

bpstrngr commented 8 months ago

This works "well" for the bundle, where the hoisted dependency can use the dependent module being higher in the bundle, while there in turn it gets copied to the local variable of that co-dependent section after a delay.

It doesn't allow using the source files directly after transpilation though, since the entry module's delay concludes before the dynamically imported dependencies would be reassigned, unlike in the bundle where the order is reversed from the "bottom up".

The entry module therefore needs to wait an extended timeout (import.meta.url==bundlename?3000:10000) until the circular imports resolve when loaded from source, than when they are hoisted in their bundle.