Mercurial > personal > weather-server
view weather_server/typescript/amd/mad-amd.ts @ 15:df3e0534c994
Tighten up MADRegistry:
- Use only one map for registry.
- Make the user construct it,
to avoid modifying global state.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 11 Oct 2019 20:50:50 -0400 |
parents | dd77a7ee02c1 |
children |
line wrap: on
line source
/** Type of the AMD factory function. */ type FactoryFunction = (...deps: unknown[]) => void; /** An individual AMD module. */ interface Module { /** The names of the module's dependencies. */ deps: string[]; /** * The function that, when called with the module's list of dependencies, * creates the module. */ factory: FactoryFunction; /** * When null, an indication that the module has not yet been reified. * When non-null, the members that the module has exported. */ exports: {}|null; } /** * Minimal AMD Dumb Registry: the dumbest possible implementation of AMD, * to handle only the code that `tsc -m amd` produces. * * Supports `require` of absolute paths and `define`s. */ class MADRegistry { /** The registry itself, mapping from name to module. */ private readonly mods = new Map<string, Module>(); /** * The (subset of) the AMD `define` function we implement. * * This supports * * - Absolute paths * - `exports`-based module construction * * @param name The name of the module to define. * @param deps The dependencies of the module. Must be explicit. * @param factory The module's factory function. */ define(name: string, deps: string[], factory: FactoryFunction) { this.mods.set(name, {deps, factory, exports: null}); } /** * The (subset of) the AMD `require` function we implement. * Only `require(dep)` is exposed to users; `require(dep, srcMod)` * is internal-only. * * - Does not support relative paths. * - Does not define `require.amd`, because we do not fully support AMD * and don't want to give the impression that we do. * * @param dep The name of the dependency. * @param srcMod The module whence the dependency was requested. * Used for when the name `exports` is required. */ require(dep: string, srcMod?: Module): {} { if (dep === 'require') { return (child: string) => this.require(child, srcMod); } if (dep === 'exports') { if (!srcMod) throw new Error('Internal consistency error.'); // We know this is safe because a module can only ever require // its own exports after it is itself required. return srcMod.exports!; } const mod = this.mods.get(dep); if (!mod) throw new Error('Undefined module.'); // If we've required the module before, return its exports. if (mod.exports) return mod.exports; // Otherwise, we need to prepare the module and require its parents. mod.exports = {}; const deps = mod.deps.map(child => this.require(child, mod)); mod.factory(...deps); return mod.exports; } /** * Installs this registry into the given object, usually `window` or `self`. * For usage with a separately-compiled JS file, do: * * ```typescript * new MADRegistry().install(); * ``` */ install(to: any) { to['define'] = (name: string, deps: string[], factory: FactoryFunction) => this.define(name, deps, factory); to['require'] = (dep: string) => this.require(dep); } }