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

pmcelhaney / counterfact / 5942566402

22 Aug 2023 06:18PM UTC coverage: 87.397% (+0.08%) from 87.318%
5942566402

Pull #377

github

pmcelhaney
put test coverage back
Pull Request #377: run CI on Windows as well as Linux

427 of 454 branches covered (94.05%)

Branch coverage included in aggregate %.

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

1910 of 2220 relevant lines covered (86.04%)

21.46 hits per line

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

90.91
/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
export class ModuleLoader extends EventTarget {
1✔
12
  basePath;
1✔
13

8✔
14
  registry;
8✔
15

8✔
16
  watcher;
8✔
17

8✔
18
  contextRegistry;
1✔
19

1✔
20
  constructor(basePath, registry, contextRegistry = new ContextRegistry()) {
1✔
21
    super();
8✔
22
    this.basePath = basePath.replaceAll("\\", "/");
8✔
23
    this.registry = registry;
8✔
24
    this.contextRegistry = contextRegistry;
8✔
25
  }
8✔
26

1✔
27
  async watch() {
1✔
28
    this.watcher = chokidar
6✔
29
      .watch(`${this.basePath}/**/*.{js,mjs,ts,mts}`, CHOKIDAR_OPTIONS)
6✔
30

6✔
31
      .on("all", (eventName, pathNameOriginal) => {
6✔
32
        const pathName = pathNameOriginal.replaceAll("\\", "/");
8✔
33

8✔
34
        if (!["add", "change", "unlink"].includes(eventName)) {
8!
35
          return;
8✔
36
        }
8✔
37

8✔
38
        const parts = nodePath.parse(pathName.replace(this.basePath, ""));
8✔
39
        const url = nodePath
8✔
40
          .normalize(`/${nodePath.join(parts.dir, parts.name)}`)
8✔
41
          .replaceAll("\\", "/");
8✔
42

8✔
43
        if (eventName === "unlink") {
8✔
44
          this.registry.remove(url);
8✔
45
          this.dispatchEvent(new Event("remove"), pathName);
8✔
46

8✔
47
          return;
8✔
48
        }
8✔
49

8✔
50
        // eslint-disable-next-line  import/no-dynamic-require, no-unsanitized/method
8✔
51
        import(`${pathName}?cacheBust=${Date.now()}`)
8✔
52
          // eslint-disable-next-line promise/prefer-await-to-then
8✔
53
          .then((endpoint) => {
8✔
54
            this.dispatchEvent(new Event(eventName), pathName);
8✔
55

8✔
56
            if (pathName.includes("$.context")) {
8!
57
              this.contextRegistry.update(parts.dir, endpoint.default);
×
58

×
59
              return "context";
×
60
            }
×
61

8✔
62
            this.registry.add(url, endpoint);
8✔
63

8✔
64
            return "path";
8✔
65
          })
8✔
66
          // eslint-disable-next-line promise/prefer-await-to-then
8✔
67
          .catch((error) => {
8✔
68
            process.stdout.write(`\nError loading ${pathName}:\n${error}\n`);
×
69
          });
×
70
      });
8✔
71

6✔
72
    await once(this.watcher, "ready");
6✔
73
  }
6✔
74

1✔
75
  async stopWatching() {
1✔
76
    await this.watcher?.close();
6✔
77
  }
6✔
78

1✔
79
  async load(directory = "") {
1✔
80
    if (
12✔
81
      !existsSync(nodePath.join(this.basePath, directory).replaceAll("\\", "/"))
12✔
82
    ) {
12!
83
      throw new Error(`Directory does not exist ${this.basePath}`);
×
84
    }
×
85

12✔
86
    const files = await fs.readdir(
12✔
87
      nodePath.join(this.basePath, directory).replaceAll("\\", "/"),
12✔
88
      {
12✔
89
        withFileTypes: true,
12✔
90
      }
12✔
91
    );
12✔
92

12✔
93
    // eslint-disable-next-line max-statements
12✔
94
    const imports = files.flatMap(async (file) => {
12✔
95
      const extension = file.name.split(".").at(-1);
16✔
96

16✔
97
      if (file.isDirectory()) {
16✔
98
        await this.load(
4✔
99
          nodePath.join(directory, file.name).replaceAll("\\", "/")
4✔
100
        );
4✔
101

4✔
102
        return;
4✔
103
      }
4✔
104

12✔
105
      if (!["js", "mjs", "ts", "mts"].includes(extension)) {
16✔
106
        return;
1✔
107
      }
1✔
108

11✔
109
      const fullPath = nodePath
11✔
110
        .join(this.basePath, directory, file.name)
11✔
111
        .replaceAll("\\", "/");
11✔
112

11✔
113
      try {
11✔
114
        // eslint-disable-next-line  import/no-dynamic-require, no-unsanitized/method
11✔
115
        const endpoint = await import(fullPath);
11✔
116

11✔
117
        if (file.name.includes("$.context")) {
16✔
118
          this.contextRegistry.add(`/${directory}`, endpoint.default);
9✔
119
        } else {
9✔
120
          this.registry.add(
9✔
121
            `/${nodePath
9✔
122
              .join(directory, nodePath.parse(file.name).name)
9✔
123
              .replaceAll("\\", "/")}`,
9✔
124
            endpoint
9✔
125
          );
9✔
126
        }
9✔
127
      } catch (error) {
16!
128
        process.stdout.write(["Error loading", fullPath, error].join("\t"));
×
129
      }
×
130
    });
16✔
131

12✔
132
    await Promise.all(imports);
12✔
133
  }
12✔
134
}
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