summaryrefslogtreecommitdiff
path: root/amd.ts
blob: 29cee123b5b89b6e94a757d80e55cb0670ee5c95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import * as path from "path";
import { assert, log } from "./util";

namespace ModuleExportsCache {
  const cache = new Map<string, object>();
  export function set(fileName: string, moduleExports: object) {
    fileName = normalizeModuleName(fileName);
    assert(
      fileName.startsWith("/"),
      `Normalized modules should start with /\n${fileName}`
    );
    log("ModuleExportsCache set", fileName);
    cache.set(fileName, moduleExports);
  }
  export function get(fileName: string): object {
    fileName = normalizeModuleName(fileName);
    log("ModuleExportsCache get", fileName);
    let moduleExports = cache.get(fileName);
    if (moduleExports == null) {
      moduleExports = {};
      set(fileName, moduleExports);
    }
    return moduleExports;
  }
}

function normalizeModuleName(fileName: string): string {
  // Remove the extension.
  return fileName.replace(/\.\w+$/, "");
}

function normalizeRelativeModuleName(contextFn: string, depFn: string): string {
  if (depFn.startsWith("/")) {
    return depFn;
  } else {
    return path.resolve(path.dirname(contextFn), depFn);
  }
}

const executeQueue: Array<() => void> = [];

export function executeQueueDrain(): void {
  let fn;
  while ((fn = executeQueue.shift())) {
    fn();
  }
}

// tslint:disable-next-line:no-any
type AmdFactory = (...args: any[]) => undefined | object;
type AmdDefine = (deps: string[], factory: AmdFactory) => void;

export function makeDefine(fileName: string): AmdDefine {
  const localDefine = (deps: string[], factory: AmdFactory): void => {
    const localRequire = (x: string) => {
      log("localRequire", x);
    };
    const localExports = ModuleExportsCache.get(fileName);
    log("localDefine", fileName, deps, localExports);
    const args = deps.map(dep => {
      if (dep === "require") {
        return localRequire;
      } else if (dep === "exports") {
        return localExports;
      } else {
        dep = normalizeRelativeModuleName(fileName, dep);
        return ModuleExportsCache.get(dep);
      }
    });
    executeQueue.push(() => {
      log("execute", fileName);
      const r = factory(...args);
      if (r != null) {
        ModuleExportsCache.set(fileName, r);
        throw Error("x");
      }
    });
  };
  return localDefine;
}