Skip to content

Instantly share code, notes, and snippets.

@tvolodimir
Last active October 29, 2020 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tvolodimir/4e38ee81a6494813a4907ba1588f14a6 to your computer and use it in GitHub Desktop.
Save tvolodimir/4e38ee81a6494813a4907ba1588f14a6 to your computer and use it in GitHub Desktop.
light require.js PoC

#require.js #PoC #topological-sorting #DAG

PoC of require.js

function isContainInArray<T>(array: T[], value: T, comparer: (a: T, b: T) => boolean): boolean {
for (let i = 0; i < array.length; i++) {
if (comparer(array[i], value)) {
return true;
}
}
return false;
}
function topologicalSortingDAG<T>(items: T[],
getDependencies: (i: T) => T[],
isSkip?: (i: T) => boolean,
order?: T[] | undefined,
isEqualFn?: (a: T, b: T) => boolean): T[] {
/*
* Topological sorting (of a DAG) using modified non-recursive Post Order DFS
* Graph traversal (Tree traversal)
*/
if (order === undefined) {
order = [];
}
if (isEqualFn === undefined) {
isEqualFn = (a, b) => a == b;
}
if (isSkip === undefined) {
isSkip = () => false;
}
for (let ni = 0; ni < items.length; ni++) {
const stack = [items[ni]];
while (stack.length > 0) {
const item = stack[stack.length - 1];
if (isContainInArray(order, item, isEqualFn)) {
stack.pop();
continue;
}
if (isSkip(item)) {
stack.pop();
continue;
}
const dependencies = getDependencies(item);
if (dependencies === null) {
throw new Error('dependencies "' + item + '" not found');
}
let existsNotResolvedDependencies = false;
for (let i = 0; i < dependencies.length; i++) {
const r = dependencies[i];
if (isContainInArray(order, r, isEqualFn)) {
continue;
}
if (isContainInArray(stack, r, isEqualFn)) {
throw new Error('circular dependency "' + r + '"');
}
stack.push(r);
existsNotResolvedDependencies = true;
break;
}
if (existsNotResolvedDependencies === false) {
order.push(item);
stack.pop();
}
}
}
return order;
}
type Factory = (m: any, r: (n: string) => any, ...args: any[]) => void;
interface Module {
assembly: Assembly;
name: string;
dependencyNames: string[];
factory: Factory;
exports?: { exports: any };
}
class Domain {
private assemblies: { [name: string]: Assembly } = {};
public defineAssembly(name: string, dependencies: string[]): Assembly {
if (domain.assemblies[name] !== undefined) {
throw new Error('[DomainManager] assembly "' + name + '" already defined');
}
return domain.assemblies[name] = new Assembly(name, dependencies);
};
public getAssembly(name: string): Assembly | undefined {
return domain.assemblies[name];
};
}
const domain = new Domain();
class Assembly {
private modules: { [n: string]: Module } = {};
constructor(public readonly name: string, public readonly dependencies: string[]) {
}
public getModule(name: string): any {
const module = this.modules[name];
if (module === undefined) {
throw new Error('[DomainManager] module "' + this.name + '.' + name + '" doesn\'t defined');
}
if (module.exports !== undefined) {
return module.exports.exports;
}
const order = topologicalSortingDAG([module], (m) => {
if (m === undefined) return [];
const dependencies: Module[] = [];
for (let i = 0; i < m.dependencyNames.length; i++) {
const module = m.assembly.findModule(m.dependencyNames[i]);
if (module === undefined) {
throw new Error('[DomainManager] module "' + this.name + '.' + name + '" not defined');
}
if (module.exports !== undefined) {
continue;
}
dependencies.push(module);
}
return dependencies;
});
for (let i = 0; i < order.length; i++) {
order[i].assembly._buildModule(order[i].name);
}
return module.exports!.exports;
}
public defineModule(name: string, dependencyNames: string[], factory: Factory): void {
if (this.modules[name] !== undefined) {
throw new Error('[DomainManager] module "' + this.name + '.' + name + '" already defined');
}
this.modules[name] = { assembly: this, name, dependencyNames, factory };
}
private _buildModule(name: string): boolean {
const module = this.modules[name];
if (module === undefined) {
throw new Error('[DomainManager] module "' + this.name + '.' + name + '" not defined');
}
if (module.exports !== undefined) {
console.log('[DomainManager] module "' + this.name + '.' + name + '" already loaded');
return true;
}
const requires = module.dependencyNames;
const factory = module.factory;
const r = [];
for (let i = 0; i < requires.length; i++) {
r.push(this.findModuleExports(requires[i]));
}
const mm = { exports: {} };
module.exports = mm;
r.unshift(mm, this.findModuleExports);
const assemblyName = this.name;
console.log('[DomainManager] loading module "' + assemblyName + '.' + name + '"');
try {
factory.apply(this, r as any);
return true;
} catch (error) {
throw new Error('[DomainManager] error loading module "' + assemblyName + '.' + name + '"')
}
}
public findModule(name: string): Module | undefined {
if (this.modules[name] !== undefined) {
return this.modules[name];
}
for (let i = 0; i < this.dependencies.length; i++) {
const assemblyName = this.dependencies[i];
const assembly = domain.getAssembly(assemblyName);
if (assembly) {
if (assembly.modules[name] !== undefined) {
return assembly.modules[name];
}
}
}
return undefined;
}
public findModuleExports: (n: string) => any = name => {
const module = this.findModule(name);
if (module === undefined) {
throw new Error('[DomainManager] for assembly "' + this.name + '" not defined "' + name + '"');
}
if (module.exports === undefined) {
throw new Error('[DomainManager] for assembly "' + this.name + '" not loaded "' + name + '"');
}
return module.exports.exports;
}
}
interface AssemblyRef {
module(name: string): any;
module(name: string, requires: string[], factory: Factory): AssemblyRef;
}
function assembly(assemblyName: string, dependencies?: string[]): AssemblyRef {
let assembly: Assembly;
if (dependencies === undefined) {
assembly = domain.getAssembly(assemblyName)!;
if (!assembly) {
throw new Error('[DomainManager] assembly "' + assemblyName + '" not defined');
}
} else {
assembly = domain.defineAssembly(assemblyName, dependencies);
}
const obj: AssemblyRef = {
module: function (name: string, requires?: string[], factory?: Factory): any {
if (requires === undefined || factory === undefined) {
return assembly.getModule(name);
} else {
assembly.defineModule(name, requires, factory);
return obj;
}
}
};
return obj;
}
function demo(): void {
assembly('first', [])
.module('a1', [], function (module) {
module.exports = 'a1';
})
.module('a2', ['a1'], function (module, $r) {
module.exports = $r('a1') + ', ' + 'a2';
});
assembly('second', ['first'])
.module('b', ['a2'], function (module, _, a2) {
module.exports = a2 + ', ' + 'b';
})
console.log(assembly('second').module('b')); // prints "a1, a2, b"
}
demo();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment