• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmcelhaney / counterfact / 5942306232

22 Aug 2023 05:51PM UTC coverage: 87.585% (+0.3%) from 87.318%
5942306232

Pull #377

github

pmcelhaney
clarify logs a bit
Pull Request #377: run CI on Windows as well as Linux

430 of 457 branches covered (94.09%)

Branch coverage included in aggregate %.

212 of 212 new or added lines in 13 files covered. (100.0%)

2004 of 2322 relevant lines covered (86.3%)

21.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.77
/src/server/module-loader.js
1
import fs from "node:fs/promises";
1✔
2
import { existsSync } from "node:fs";
1✔
3
import nodePath from "node:path";
1✔
4
import { once } from "node:events";
1✔
5

1✔
6
import chokidar from "chokidar";
1✔
7

1✔
8
import { ContextRegistry } from "./context-registry.js";
1✔
9
import { CHOKIDAR_OPTIONS } from "./constants.js";
1✔
10

1✔
11
function log(...strings) {
70✔
12
  process.stdout.write(`[module-loader] ${strings.join("\t")}\n`);
70✔
13
}
70✔
14

1✔
15
export class ModuleLoader extends EventTarget {
1✔
16
  basePath;
1✔
17

8✔
18
  registry;
8✔
19

8✔
20
  watcher;
8✔
21

8✔
22
  contextRegistry;
1✔
23

1✔
24
  constructor(basePath, registry, contextRegistry = new ContextRegistry()) {
1✔
25
    super();
8✔
26
    this.basePath = basePath.replace("\\", "/", "all");
8✔
27
    this.registry = registry;
8✔
28
    this.contextRegistry = contextRegistry;
8✔
29
  }
8✔
30

1✔
31
  async watch() {
1✔
32
    log("watching", this.basePath);
6✔
33
    this.watcher = chokidar
6✔
34
      .watch(`${this.basePath}/**/*.{js,mjs,ts,mts}`, CHOKIDAR_OPTIONS)
6✔
35
      // eslint-disable-next-line max-statements
6✔
36
      .on("all", (eventName, pathNameOriginal) => {
6✔
37
        const pathName = pathNameOriginal.replaceAll("\\", "/");
9✔
38

9✔
39
        log("chokidar", eventName, pathName);
9✔
40

9✔
41
        if (!["add", "change", "unlink"].includes(eventName)) {
9!
42
          return;
×
43
        }
×
44

9✔
45
        const parts = nodePath.parse(pathName.replace(this.basePath, ""));
9✔
46
        const url = nodePath
9✔
47
          .normalize(`/${nodePath.join(parts.dir, parts.name)}`)
9✔
48
          .replaceAll("\\", "/");
9✔
49

9✔
50
        if (eventName === "unlink") {
9✔
51
          log("removing a module from registry at runtime", url);
1✔
52
          this.registry.remove(url);
1✔
53
          log("did remove a module from registry", url);
1✔
54
          this.dispatchEvent(new Event("remove"), pathName);
1✔
55
        }
1✔
56

9✔
57
        // eslint-disable-next-line  import/no-dynamic-require, no-unsanitized/method
9✔
58
        import(`${pathName}?cacheBust=${Date.now()}`)
9✔
59
          // eslint-disable-next-line promise/prefer-await-to-then
9✔
60
          .then((endpoint) => {
9✔
61
            this.dispatchEvent(new Event(eventName), pathName);
9✔
62

9✔
63
            if (pathName.includes("$.context")) {
9!
64
              this.contextRegistry.update(parts.dir, endpoint.default);
×
65

×
66
              return "context";
×
67
            }
×
68

9✔
69
            log("adding module to registry at runtime", url);
9✔
70
            this.registry.add(url, endpoint);
9✔
71
            log("did add a module to the registry", url);
9✔
72

9✔
73
            return "path";
9✔
74
          })
9✔
75
          // eslint-disable-next-line promise/prefer-await-to-then
9✔
76
          .catch((error) => {
9✔
77
            process.stdout.write(`\nError loading ${pathName}:\n${error}\n`);
×
78
          });
×
79
      });
9✔
80

6✔
81
    log("waiting for ready event", this.basePath);
6✔
82
    await once(this.watcher, "ready");
6✔
83
    log("received ready event", this.basePath);
6✔
84
  }
6✔
85

1✔
86
  async stopWatching() {
1✔
87
    log("stopping the watcher...", this.basePath);
6✔
88
    await this.watcher?.close();
6✔
89
    log("stopped the watcher", this.basePath);
6✔
90
  }
6✔
91

1✔
92
  async load(directory = "") {
1✔
93
    if (
12✔
94
      !existsSync(nodePath.join(this.basePath, directory).replaceAll("\\", "/"))
12✔
95
    ) {
12!
96
      log("Directory does not exist", this.basePath, directory);
×
97

×
98
      throw new Error(`Directory does not exist ${this.basePath}`);
×
99
    }
×
100

12✔
101
    const files = await fs.readdir(
12✔
102
      nodePath.join(this.basePath, directory).replaceAll("\\", "/"),
12✔
103
      {
12✔
104
        withFileTypes: true,
12✔
105
      }
12✔
106
    );
12✔
107

12✔
108
    // eslint-disable-next-line max-statements
12✔
109
    const imports = files.flatMap(async (file) => {
12✔
110
      const extension = file.name.split(".").at(-1);
16✔
111

16✔
112
      if (file.isDirectory()) {
16✔
113
        await this.load(
4✔
114
          nodePath.join(directory, file.name).replaceAll("\\", "/")
4✔
115
        );
4✔
116

4✔
117
        return;
4✔
118
      }
4✔
119

12✔
120
      if (!["js", "mjs", "ts", "mts"].includes(extension)) {
16✔
121
        return;
1✔
122
      }
1✔
123

11✔
124
      const fullPath = nodePath
11✔
125
        .join(this.basePath, directory, file.name)
11✔
126
        .replaceAll("\\", "/");
11✔
127

11✔
128
      try {
11✔
129
        // eslint-disable-next-line  import/no-dynamic-require, no-unsanitized/method
11✔
130
        const endpoint = await import(fullPath);
11✔
131

11✔
132
        if (file.name.includes("$.context")) {
16✔
133
          log("adding context to registry", directory, endpoint.default);
9✔
134
          this.contextRegistry.add(`/${directory}`, endpoint.default);
9✔
135
        } else {
9✔
136
          log(
9✔
137
            "adding module to registry",
9✔
138
            directory,
9✔
139
            file.name,
9✔
140
            endpoint.default
9✔
141
          );
9✔
142
          this.registry.add(
9✔
143
            `/${nodePath
9✔
144
              .join(directory, nodePath.parse(file.name).name)
9✔
145
              .replaceAll("\\", "/")}`,
9✔
146
            endpoint
9✔
147
          );
9✔
148
        }
9✔
149
      } catch (error) {
16!
150
        log("Error loading", fullPath, error);
×
151
      }
×
152
    });
16✔
153

12✔
154
    await Promise.all(imports);
12✔
155
  }
12✔
156
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc