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.
- package/bin/counterfact.js +102 -6
- package/dist/server/app.js +15 -10
- package/dist/server/convert-js-extensions-to-cjs.js +3 -1
- package/dist/{server → typescript-generator}/code-generator.js +11 -6
- package/dist/typescript-generator/generate.js +3 -9
- package/dist/typescript-generator/repository.js +13 -6
- package/package.json +5 -5
package/bin/counterfact.js
CHANGED
|
@@ -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
|
-
|
|
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>",
|
package/dist/server/app.js
CHANGED
|
@@ -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
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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 (
|
|
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(
|
|
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 {
|
|
2
|
+
import { CHOKIDAR_OPTIONS } from "../server/constants.js";
|
|
3
3
|
import { waitForEvent } from "../util/wait-for-event.js";
|
|
4
|
-
import {
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
105
|
+
"recast": "0.23.7",
|
|
106
106
|
"typescript": "5.4.5"
|
|
107
107
|
}
|
|
108
108
|
}
|