counterfact 0.41.1 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable complexity */
2
3
 
3
4
  import { readFile } from "node:fs/promises";
4
5
  import nodePath from "node:path";
@@ -43,11 +44,66 @@ function padTagLine(tagLine) {
43
44
  return `${padding}${tagLine}`;
44
45
  }
45
46
 
47
+ // eslint-disable-next-line max-statements
48
+ function createWatchMessage(config) {
49
+ let watchMessage = "";
50
+
51
+ switch (true) {
52
+ case config.watch.routes && config.watch.types: {
53
+ watchMessage = "Watching for changes";
54
+
55
+ break;
56
+ }
57
+ case config.watch.routes: {
58
+ watchMessage = "Watching routes for changes";
59
+
60
+ break;
61
+ }
62
+ case config.watch.types: {
63
+ watchMessage = "Watching types for changes";
64
+
65
+ break;
66
+ }
67
+
68
+ default: {
69
+ watchMessage = undefined;
70
+ }
71
+ }
72
+
73
+ if (!watchMessage) {
74
+ switch (true) {
75
+ case config.generate.routes && config.generate.types: {
76
+ watchMessage = "Generating routes and types";
77
+
78
+ break;
79
+ }
80
+ case config.generate.routes: {
81
+ watchMessage = "Generating routes";
82
+
83
+ break;
84
+ }
85
+ case config.generate.types: {
86
+ watchMessage = "Generating types";
87
+
88
+ break;
89
+ }
90
+
91
+ default: {
92
+ watchMessage = undefined;
93
+ }
94
+ }
95
+ }
96
+
97
+ return watchMessage;
98
+ }
99
+
46
100
  // eslint-disable-next-line max-statements
47
101
  async function main(source, destination) {
48
102
  debug("executing the main function");
49
103
 
50
104
  const options = program.opts();
105
+ // eslint-disable-next-line sonar/process-argv
106
+ const args = process.argv;
51
107
 
52
108
  const destinationPath = nodePath
53
109
  .join(process.cwd(), destination)
@@ -55,6 +111,14 @@ async function main(source, destination) {
55
111
 
56
112
  const basePath = nodePath.resolve(destinationPath).replaceAll("\\", "/");
57
113
 
114
+ // If no options are provided, default to all options
115
+ if (!args.some((argument) => argument.startsWith("-"))) {
116
+ options.repl = true;
117
+ options.serve = true;
118
+ options.watch = true;
119
+ options.generate = true;
120
+ }
121
+
58
122
  debug("options: %o", options);
59
123
  debug("source: %s", source);
60
124
  debug("destination: %s", destination);
@@ -69,12 +133,34 @@ async function main(source, destination) {
69
133
 
70
134
  const config = {
71
135
  basePath,
136
+
137
+ generate: {
138
+ routes:
139
+ options.generate ||
140
+ options.generateRoutes ||
141
+ options.watch ||
142
+ options.watchRoutes,
143
+
144
+ types:
145
+ options.generate ||
146
+ options.generateTypes ||
147
+ options.watch ||
148
+ options.watchTypes,
149
+ },
150
+
72
151
  includeSwaggerUi: true,
73
152
  openApiPath: source,
74
153
  port: options.port,
75
154
  proxyEnabled: Boolean(options.proxyUrl),
76
155
  proxyUrl: options.proxyUrl,
77
156
  routePrefix: options.prefix,
157
+ startRepl: options.repl,
158
+ startServer: options.serve,
159
+
160
+ watch: {
161
+ routes: options.watch || options.watchRoutes,
162
+ types: options.watch || options.watchTypes,
163
+ },
78
164
  };
79
165
 
80
166
  debug("loading counterfact (%o)", config);
@@ -83,6 +169,8 @@ async function main(source, destination) {
83
169
 
84
170
  debug("loaded counterfact", config);
85
171
 
172
+ const watchMessage = createWatchMessage(config);
173
+
86
174
  const introduction = [
87
175
  "____ ____ _ _ _ _ ___ ____ ____ ____ ____ ____ ___",
88
176
  "|___ [__] |__| |\\| | |=== |--< |--- |--| |___ | ",
@@ -97,7 +185,9 @@ async function main(source, destination) {
97
185
  "🎉 VERSION 1.0 IS COMING! LEARN MORE:",
98
186
  " https://github.com/pmcelhaney/counterfact/issues/823",
99
187
  "",
100
- "Starting REPL, type .help for more info",
188
+ watchMessage,
189
+ config.startServer ? "Starting server" : undefined,
190
+ config.startRepl ? "Starting REPL, type .help for more info" : undefined,
101
191
  ];
102
192
 
103
193
  process.stdout.write(
@@ -107,9 +197,7 @@ async function main(source, destination) {
107
197
  process.stdout.write("\n\n");
108
198
 
109
199
  debug("starting server");
110
-
111
- await start();
112
-
200
+ await start(config);
113
201
  debug("started server");
114
202
 
115
203
  if (openBrowser) {
@@ -130,9 +218,17 @@ program
130
218
  "_",
131
219
  )
132
220
  .argument("[destination]", "path to generated code", ".")
133
- .option("--port <number>", "server port number", DEFAULT_PORT)
221
+ .option("-p, --port <number>", "server port number", DEFAULT_PORT)
134
222
  .option("--swagger", "include swagger-ui")
135
- .option("--open", "open a browser")
223
+ .option("-o, --open", "open a browser")
224
+ .option("-g, --generate", "generate all code for both routes and types")
225
+ .option("--generate-types", "generate types")
226
+ .option("--generate-routes", "generate routes")
227
+ .option("-w, --watch", "generate + watch all code for changes")
228
+ .option("--watch-types", "generate + watch types for changes")
229
+ .option("--watch-routes", "generate + watch routes for changes")
230
+ .option("-s, --serve", "start the server")
231
+ .option("-r, --repl", "start the REPL")
136
232
  .option("--proxy-url <string>", "proxy URL")
137
233
  .option(
138
234
  "--prefix <string>",
@@ -3,8 +3,8 @@ import nodePath from "node:path";
3
3
  import { createHttpTerminator } from "http-terminator";
4
4
  import yaml from "js-yaml";
5
5
  import $RefParser from "json-schema-ref-parser";
6
+ import { CodeGenerator } from "../typescript-generator/code-generator.js";
6
7
  import { readFile } from "../util/read-file.js";
7
- import { CodeGenerator } from "./code-generator.js";
8
8
  import { ContextRegistry } from "./context-registry.js";
9
9
  import { createKoaApp } from "./create-koa-app.js";
10
10
  import { Dispatcher } from "./dispatcher.js";
@@ -34,22 +34,27 @@ export async function counterfact(config) {
34
34
  await rm(compiledPathsDirectory, { force: true, recursive: true });
35
35
  const registry = new Registry();
36
36
  const contextRegistry = new ContextRegistry();
37
- const codeGenerator = new CodeGenerator(config.openApiPath, config.basePath);
37
+ const codeGenerator = new CodeGenerator(config.openApiPath, config.basePath, config.generate);
38
38
  const dispatcher = new Dispatcher(registry, contextRegistry, await loadOpenApiDocument(config.openApiPath));
39
39
  const transpiler = new Transpiler(nodePath.join(modulesPath, "paths").replaceAll("\\", "/"), compiledPathsDirectory, "commonjs");
40
40
  const moduleLoader = new ModuleLoader(compiledPathsDirectory, registry, contextRegistry);
41
41
  const middleware = koaMiddleware(dispatcher, config);
42
42
  const koaApp = createKoaApp(registry, middleware, config);
43
43
  // eslint-disable-next-line max-statements
44
- async function start(options = {}) {
45
- const http = options.http ?? true;
46
- await codeGenerator.watch();
47
- await transpiler.watch();
48
- await moduleLoader.load();
49
- await moduleLoader.watch();
44
+ async function start(options) {
45
+ const { generate, startRepl: shouldStartRepl, startServer, watch, } = options;
46
+ if (generate.routes || generate.types) {
47
+ await codeGenerator.generate();
48
+ }
49
+ if (watch.routes || watch.types) {
50
+ await codeGenerator.watch();
51
+ }
50
52
  // eslint-disable-next-line @typescript-eslint/init-declarations
51
53
  let httpTerminator;
52
- if (http) {
54
+ if (startServer) {
55
+ await transpiler.watch();
56
+ await moduleLoader.load();
57
+ await moduleLoader.watch();
53
58
  const server = koaApp.listen({
54
59
  port: config.port,
55
60
  });
@@ -57,7 +62,7 @@ export async function counterfact(config) {
57
62
  server,
58
63
  });
59
64
  }
60
- const replServer = startRepl(contextRegistry, config);
65
+ const replServer = shouldStartRepl && startRepl(contextRegistry, config);
61
66
  return {
62
67
  replServer,
63
68
  async stop() {
@@ -17,7 +17,9 @@ export function convertFileExtensionsToCjs(code) {
17
17
  typeof node.arguments[0].value === "string" &&
18
18
  node.arguments[0].value.startsWith(".")) {
19
19
  // Change the module string from "foo.js" to "foo.cjs"
20
- node.arguments[0].value = node.arguments[0].value.replace(/\.js$/u, ".cjs");
20
+ node.arguments[0].value = node.arguments[0].value.replace(
21
+ // eslint-disable-next-line prefer-named-capture-group, regexp/no-unused-capturing-group, regexp/prefer-named-capture-group
22
+ /(\.js|\.ts)?$/u, ".cjs");
21
23
  }
22
24
  // Continue traversing the AST
23
25
  this.traverse(path);
@@ -1,24 +1,29 @@
1
1
  import { watch } from "chokidar";
2
- import { generate } from "../typescript-generator/generate.js";
2
+ import { CHOKIDAR_OPTIONS } from "../server/constants.js";
3
3
  import { waitForEvent } from "../util/wait-for-event.js";
4
- import { CHOKIDAR_OPTIONS } from "./constants.js";
4
+ import { generate } from "./generate.js";
5
5
  export class CodeGenerator extends EventTarget {
6
6
  openapiPath;
7
7
  destination;
8
+ generateOptions;
8
9
  watcher;
9
- constructor(openApiPath, destination) {
10
+ constructor(openApiPath, destination, generateOptions) {
10
11
  super();
11
12
  this.openapiPath = openApiPath;
12
13
  this.destination = destination;
14
+ this.generateOptions = generateOptions;
15
+ }
16
+ async generate() {
17
+ await generate(this.openapiPath, this.destination, this.generateOptions);
13
18
  }
14
19
  async watch() {
15
- await generate(this.openapiPath, this.destination);
16
20
  if (this.openapiPath.startsWith("http")) {
17
21
  return;
18
22
  }
19
23
  this.watcher = watch(this.openapiPath, CHOKIDAR_OPTIONS).on("change", () => {
20
- // eslint-disable-next-line promise/prefer-await-to-then
21
- void generate(this.openapiPath, this.destination).then(() => {
24
+ void generate(this.openapiPath, this.destination, this.generateOptions)
25
+ // eslint-disable-next-line promise/prefer-await-to-then
26
+ .then(() => {
22
27
  this.dispatchEvent(new Event("generate"));
23
28
  return true;
24
29
  }, () => {
@@ -7,15 +7,9 @@ import { OperationCoder } from "./operation-coder.js";
7
7
  import { Repository } from "./repository.js";
8
8
  import { Specification } from "./specification.js";
9
9
  const debug = createDebug("counterfact:typescript-generator:generate");
10
- // eslint-disable-next-line max-statements
11
10
  async function buildCacheDirectory(destination) {
12
11
  const gitignorePath = nodePath.join(destination, ".gitignore");
13
12
  const cacheReadmePath = nodePath.join(destination, ".cache", "README.md");
14
- debug("removing the cache directory");
15
- await fs.rm(nodePath.join(destination, ".cache"), {
16
- force: true,
17
- recursive: true,
18
- });
19
13
  debug("ensuring the directory containing .gitgnore exists");
20
14
  await ensureDirectoryExists(gitignorePath);
21
15
  debug("creating the .gitignore file if it doesn't already exist");
@@ -35,8 +29,8 @@ async function getPathsFromSpecification(specification) {
35
29
  return new Set();
36
30
  }
37
31
  }
38
- // eslint-disable-next-line max-statements
39
- export async function generate(source, destination, repository = new Repository()) {
32
+ // eslint-disable-next-line max-statements, max-params
33
+ export async function generate(source, destination, generateOptions, repository = new Repository()) {
40
34
  debug("generating code from %s to %s", source, destination);
41
35
  debug("initializing the .cache directory");
42
36
  await buildCacheDirectory(destination);
@@ -58,6 +52,6 @@ export async function generate(source, destination, repository = new Repository(
58
52
  });
59
53
  });
60
54
  debug("telling the repository to write the files to %s", destination);
61
- await repository.writeFiles(destination);
55
+ await repository.writeFiles(destination, generateOptions);
62
56
  debug("finished writing the files");
63
57
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-statements */
1
2
  import { existsSync } from "node:fs";
2
3
  import fs from "node:fs/promises";
3
4
  import nodePath, { dirname } from "node:path";
@@ -41,7 +42,7 @@ export class Repository {
41
42
  await ensureDirectoryExists(destination);
42
43
  return fs.copyFile(sourcePath, destinationPath);
43
44
  }
44
- async writeFiles(destination) {
45
+ async writeFiles(destination, { routes, types }) {
45
46
  debug("waiting for %i or more scripts to finish before writing files", this.scripts.size);
46
47
  await this.finished();
47
48
  debug("all %i scripts are finished", this.scripts.size);
@@ -49,7 +50,9 @@ export class Repository {
49
50
  const contents = await script.contents();
50
51
  const fullPath = nodePath.join(destination, path).replaceAll("\\", "/");
51
52
  await ensureDirectoryExists(fullPath);
52
- if (path.startsWith("paths") &&
53
+ const shouldWriteRoutes = routes && path.startsWith("paths");
54
+ const shouldWriteTypes = types && !path.startsWith("paths");
55
+ if (shouldWriteRoutes &&
53
56
  (await fs
54
57
  .stat(fullPath)
55
58
  .then((stat) => stat.isFile())
@@ -57,13 +60,17 @@ export class Repository {
57
60
  debug(`not overwriting ${fullPath}\n`);
58
61
  return;
59
62
  }
60
- debug("about to write", fullPath);
61
- await fs.writeFile(fullPath, contents.replaceAll(CONTEXT_FILE_TOKEN, this.findContextPath(destination, path)));
62
- debug("did write", fullPath);
63
+ if (shouldWriteRoutes || shouldWriteTypes) {
64
+ debug("about to write", fullPath);
65
+ await fs.writeFile(fullPath, contents.replaceAll(CONTEXT_FILE_TOKEN, this.findContextPath(destination, path)));
66
+ debug("did write", fullPath);
67
+ }
63
68
  });
64
69
  await Promise.all(writeFiles);
65
70
  await this.copyCoreFiles(destination);
66
- await this.createDefaultContextFile(destination);
71
+ if (routes) {
72
+ await this.createDefaultContextFile(destination);
73
+ }
67
74
  }
68
75
  async createDefaultContextFile(destination) {
69
76
  const contextFilePath = nodePath.join(destination, "paths", "_.context.ts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "counterfact",
3
- "version": "0.41.1",
3
+ "version": "0.42.1",
4
4
  "description": "a library for building a fake REST API for testing",
5
5
  "type": "module",
6
6
  "main": "./src/server/counterfact.js",
@@ -48,7 +48,7 @@
48
48
  "@stryker-mutator/core": "8.2.6",
49
49
  "@stryker-mutator/jest-runner": "8.2.6",
50
50
  "@stryker-mutator/typescript-checker": "8.2.6",
51
- "@swc/core": "1.5.0",
51
+ "@swc/core": "1.5.5",
52
52
  "@swc/jest": "0.2.36",
53
53
  "@testing-library/dom": "10.1.0",
54
54
  "@types/jest": "29.5.12",
@@ -63,7 +63,7 @@
63
63
  "eslint-formatter-github-annotations": "0.1.0",
64
64
  "eslint-import-resolver-typescript": "3.6.1",
65
65
  "eslint-plugin-etc": "2.0.3",
66
- "eslint-plugin-file-progress": "1.3.0",
66
+ "eslint-plugin-file-progress": "1.4.0",
67
67
  "eslint-plugin-import": "2.29.1",
68
68
  "eslint-plugin-jest": "28.5.0",
69
69
  "eslint-plugin-jest-dom": "5.4.0",
@@ -73,7 +73,7 @@
73
73
  "jest": "29.7.0",
74
74
  "node-mocks-http": "1.14.1",
75
75
  "nodemon": "3.1.0",
76
- "rimraf": "5.0.5",
76
+ "rimraf": "5.0.7",
77
77
  "stryker-cli": "1.0.2",
78
78
  "supertest": "7.0.0",
79
79
  "using-temporary-files": "2.2.1"
@@ -102,7 +102,7 @@
102
102
  "patch-package": "8.0.0",
103
103
  "precinct": "12.1.1",
104
104
  "prettier": "3.2.5",
105
- "recast": "0.23.6",
105
+ "recast": "0.23.7",
106
106
  "typescript": "5.4.5"
107
107
  }
108
108
  }