@fedify/cli 2.0.0-pr.474.1879 → 2.0.0-pr.479.1900
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/deno.json +1 -1
- package/dist/deno.js +1 -1
- package/dist/imagerenderer.js +1 -1
- package/dist/inbox.js +2 -5
- package/dist/init/action/configs.js +22 -13
- package/dist/init/action/const.js +10 -0
- package/dist/init/action/deps.js +26 -28
- package/dist/init/action/install.js +11 -13
- package/dist/init/action/mod.js +1 -1
- package/dist/init/action/notice.js +12 -19
- package/dist/init/action/patch.js +33 -27
- package/dist/init/action/precommand.js +7 -2
- package/dist/init/action/recommend.js +1 -1
- package/dist/init/action/set.js +1 -1
- package/dist/init/action/templates.js +2 -1
- package/dist/init/ask/kv.js +24 -13
- package/dist/init/ask/mq.js +26 -13
- package/dist/init/command.js +20 -3
- package/dist/init/lib.js +20 -13
- package/dist/init/mod.js +2 -1
- package/dist/init/templates/nitro/.env.test.tpl +1 -0
- package/dist/init/templates/nitro/nitro.config.ts.tpl +12 -3
- package/dist/init/test/action.js +15 -0
- package/dist/init/test/create.js +100 -0
- package/dist/init/test/fill.js +26 -0
- package/dist/init/test/lookup.js +189 -0
- package/dist/init/test/run.js +26 -0
- package/dist/init/test/utils.js +17 -0
- package/dist/init/webframeworks.js +31 -28
- package/dist/mod.js +4 -2
- package/dist/nodeinfo.js +6 -6
- package/dist/utils.js +75 -8
- package/package.json +5 -5
- package/src/inbox.tsx +1 -15
- package/src/init/action/configs.ts +56 -13
- package/src/init/action/const.ts +9 -0
- package/src/init/action/deps.ts +98 -30
- package/src/init/action/install.ts +17 -52
- package/src/init/action/notice.ts +16 -14
- package/src/init/action/patch.ts +48 -27
- package/src/init/action/precommand.ts +9 -2
- package/src/init/action/set.ts +2 -15
- package/src/init/action/templates.ts +3 -2
- package/src/init/action/utils.ts +1 -1
- package/src/init/ask/kv.ts +64 -21
- package/src/init/ask/mq.ts +69 -20
- package/src/init/command.ts +39 -1
- package/src/init/lib.ts +31 -28
- package/src/init/mod.ts +2 -1
- package/src/init/templates/nitro/.env.test.tpl +1 -0
- package/src/init/templates/nitro/nitro.config.ts.tpl +12 -3
- package/src/init/test/action.ts +25 -0
- package/src/init/test/create.ts +137 -0
- package/src/init/test/fill.ts +61 -0
- package/src/init/test/lookup.ts +253 -0
- package/src/init/test/run.ts +42 -0
- package/src/init/test/types.ts +34 -0
- package/src/init/test/utils.ts +21 -0
- package/src/init/types.ts +4 -3
- package/src/init/webframeworks.ts +35 -18
- package/src/mod.ts +10 -1
- package/src/nodeinfo.ts +2 -1
- package/src/utils.ts +128 -10
package/dist/init/command.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { debugOption } from "../globals.js";
|
|
5
5
|
import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK } from "./const.js";
|
|
6
|
-
import { argument, choice, command, constant, message, object, option, optionNames, optional } from "@optique/core";
|
|
6
|
+
import { argument, choice, command, constant, message, multiple, object, option, optionNames, optional } from "@optique/core";
|
|
7
7
|
import { path } from "@optique/run";
|
|
8
8
|
|
|
9
9
|
//#region src/init/command.ts
|
|
@@ -11,6 +11,7 @@ const webFramework = optional(option("-w", "--web-framework", choice(WEB_FRAMEWO
|
|
|
11
11
|
const packageManager = optional(option("-p", "--package-manager", choice(PACKAGE_MANAGER, { metavar: "PACKAGE_MANAGER" }), { description: message`The package manager to use for installing dependencies.` }));
|
|
12
12
|
const kvStore = optional(option("-k", "--kv-store", choice(KV_STORE, { metavar: "KV_STORE" }), { description: message`The key-value store to use for caching and some other features.` }));
|
|
13
13
|
const messageQueue = optional(option("-m", "--message-queue", choice(MESSAGE_QUEUE, { metavar: "MESSAGE_QUEUE" }), { description: message`The message queue to use for background tasks.` }));
|
|
14
|
+
const testMode = option("--test-mode", { description: message`The test mode to use for testing purposes.` });
|
|
14
15
|
const initCommand = command("init", object("Initialization options", {
|
|
15
16
|
command: constant("init"),
|
|
16
17
|
dir: optional(argument(path({ metavar: "DIR" }), { description: message`The project directory to initialize. If a specified directory does not exist, it will be created.` })),
|
|
@@ -19,7 +20,8 @@ const initCommand = command("init", object("Initialization options", {
|
|
|
19
20
|
kvStore,
|
|
20
21
|
messageQueue,
|
|
21
22
|
dryRun: option("-d", "--dry-run", { description: message`Perform a trial run with no changes made.` }),
|
|
22
|
-
debugOption
|
|
23
|
+
debugOption,
|
|
24
|
+
testMode
|
|
23
25
|
}), {
|
|
24
26
|
brief: message`Initialize a new Fedify project directory.`,
|
|
25
27
|
description: message`Initialize a new Fedify project directory.
|
|
@@ -28,6 +30,21 @@ By default, it initializes the current directory. You can specify a different d
|
|
|
28
30
|
|
|
29
31
|
Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${optionNames(["-p", "--package-manager"])}, ${optionNames(["-k", "--kv-store"])}, and ${optionNames(["-m", "--message-queue"])}), it will prompt you to select the options interactively.`
|
|
30
32
|
});
|
|
33
|
+
const testInitCommand = command("test-init", object("Initialization options", {
|
|
34
|
+
command: constant("test-init"),
|
|
35
|
+
webFramework: multiple(webFramework),
|
|
36
|
+
packageManager: multiple(packageManager),
|
|
37
|
+
kvStore: multiple(kvStore),
|
|
38
|
+
messageQueue: multiple(messageQueue),
|
|
39
|
+
hydRun: option("-h", "--hyd-run", { description: message`Test with files creations and installations.` }),
|
|
40
|
+
dryRun: option("-d", "--dry-run", { description: message`Log outputs without creating files.` }),
|
|
41
|
+
debugOption
|
|
42
|
+
}), {
|
|
43
|
+
brief: message`Test an initializing command .`,
|
|
44
|
+
description: message`Test an initializing command on temporary directories.
|
|
45
|
+
|
|
46
|
+
Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${optionNames(["-p", "--package-manager"])}, ${optionNames(["-k", "--kv-store"])}, and ${optionNames(["-m", "--message-queue"])}), it will test all combinations of the options.`
|
|
47
|
+
});
|
|
31
48
|
|
|
32
49
|
//#endregion
|
|
33
|
-
export { initCommand };
|
|
50
|
+
export { initCommand, testInitCommand };
|
package/dist/init/lib.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Temporal } from "@js-temporal/polyfill";
|
|
3
3
|
|
|
4
4
|
import deno_default from "../deno.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isNotFoundError, runSubCommand } from "../utils.js";
|
|
6
6
|
import kv_default from "./json/kv.js";
|
|
7
7
|
import mq_default from "./json/mq.js";
|
|
8
8
|
import pm_default from "./json/pm.js";
|
|
@@ -10,6 +10,7 @@ import rt_default from "./json/rt.js";
|
|
|
10
10
|
import webframeworks_default from "./webframeworks.js";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
13
|
+
import { commandLine, message } from "@optique/core/message";
|
|
13
14
|
import process from "node:process";
|
|
14
15
|
import { getLogger } from "@logtape/logtape";
|
|
15
16
|
import { entries, evolve, fromEntries, isObject, map, negate, pipe, throwIf, when } from "@fxts/core";
|
|
@@ -44,17 +45,17 @@ async function isPackageManagerAvailable(pm) {
|
|
|
44
45
|
return false;
|
|
45
46
|
}
|
|
46
47
|
const readTemplate = (templatePath) => readFileSync(join(import.meta.dirname, "templates", ...(templatePath + ".tpl").split("/")), "utf8");
|
|
47
|
-
const getInstruction = (pm) => `
|
|
48
|
+
const getInstruction = (pm, port) => message`
|
|
48
49
|
To start the server, run the following command:
|
|
49
50
|
|
|
50
|
-
${getDevCommand(pm)}
|
|
51
|
+
${commandLine(getDevCommand(pm))}
|
|
51
52
|
|
|
52
53
|
Then, try look up an actor from your server:
|
|
53
54
|
|
|
54
|
-
${
|
|
55
|
+
${commandLine(`fedify lookup http://localhost:${port}/users/john`)}
|
|
55
56
|
|
|
56
57
|
`;
|
|
57
|
-
const getDevCommand = (pm) =>
|
|
58
|
+
const getDevCommand = (pm) => pm === "deno" ? "deno task dev" : pm === "bun" ? "bun dev" : `${pm} run dev`;
|
|
58
59
|
async function isCommandAvailable({ checkCommand, outputPattern }) {
|
|
59
60
|
try {
|
|
60
61
|
const { stdout } = await runSubCommand(checkCommand, { stdio: [
|
|
@@ -91,18 +92,20 @@ const isDirectoryEmpty = async (path$1) => {
|
|
|
91
92
|
return true;
|
|
92
93
|
}
|
|
93
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Converts a package manager to its corresponding runtime.
|
|
97
|
+
* @param pm - The package manager (deno, bun, npm, yarn, pnpm)
|
|
98
|
+
* @returns The runtime name (deno, bun, or node)
|
|
99
|
+
*/
|
|
100
|
+
const packageManagerToRuntime = (pm) => pm === "deno" ? "deno" : pm === "bun" ? "bun" : "node";
|
|
94
101
|
const getNextInitCommand = (pm) => [
|
|
95
102
|
...createNextAppCommand(pm),
|
|
96
103
|
".",
|
|
97
|
-
"--
|
|
98
|
-
"--app",
|
|
99
|
-
"--biome",
|
|
100
|
-
"--skip-install"
|
|
104
|
+
"--yes"
|
|
101
105
|
];
|
|
102
106
|
const createNextAppCommand = (pm) => pm === "deno" ? [
|
|
103
107
|
"deno",
|
|
104
|
-
"
|
|
105
|
-
"-A",
|
|
108
|
+
"-Ar",
|
|
106
109
|
"npm:create-next-app@latest"
|
|
107
110
|
] : pm === "bun" ? [
|
|
108
111
|
"bun",
|
|
@@ -117,13 +120,17 @@ const getNitroInitCommand = (pm) => [
|
|
|
117
120
|
...createNitroAppCommand(pm),
|
|
118
121
|
pm === "deno" ? "npm:giget@latest" : "giget@latest",
|
|
119
122
|
"nitro",
|
|
120
|
-
"."
|
|
123
|
+
".",
|
|
124
|
+
"&&",
|
|
125
|
+
"rm",
|
|
126
|
+
"nitro.config.ts"
|
|
121
127
|
];
|
|
122
128
|
const createNitroAppCommand = (pm) => pm === "deno" ? [
|
|
123
129
|
"deno",
|
|
124
130
|
"run",
|
|
125
131
|
"-A"
|
|
126
132
|
] : pm === "bun" ? ["bunx"] : pm === "npm" ? ["npx"] : [pm, "dlx"];
|
|
133
|
+
const isTest = ({ testMode }) => testMode;
|
|
127
134
|
|
|
128
135
|
//#endregion
|
|
129
|
-
export { PACKAGE_VERSION, createFile, getInstallUrl, getInstruction, getLabel, getNextInitCommand, getNitroInitCommand, isDirectoryEmpty, isPackageManagerAvailable, kvStores, logger, messageQueues, readTemplate, throwUnlessNotExists };
|
|
136
|
+
export { PACKAGE_VERSION, createFile, getDevCommand, getInstallUrl, getInstruction, getLabel, getNextInitCommand, getNitroInitCommand, isDirectoryEmpty, isPackageManagerAvailable, isTest, kvStores, logger, messageQueues, packageManagerToRuntime, readTemplate, throwUnlessNotExists };
|
package/dist/init/mod.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
HOST=127.0.0.1
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { defineNitroConfig } from "nitropack/config"
|
|
2
|
+
|
|
3
|
+
// https://nitro.build/config
|
|
2
4
|
export default defineNitroConfig({
|
|
5
|
+
errorHandler: "~/error",
|
|
6
|
+
esbuild: {
|
|
7
|
+
options: {
|
|
8
|
+
target: "esnext",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
compatibilityDate: "latest",
|
|
3
12
|
srcDir: "server",
|
|
4
|
-
|
|
5
|
-
});
|
|
13
|
+
imports: false
|
|
14
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { set } from "../../utils.js";
|
|
5
|
+
import { fillEmptyOptions } from "./fill.js";
|
|
6
|
+
import { isDryRun, isHydRun, runTests } from "./run.js";
|
|
7
|
+
import { emptyTestDir, genRunId, genTestDirPrefix, logTestDir } from "./utils.js";
|
|
8
|
+
import { pipe, tap, when } from "@fxts/core";
|
|
9
|
+
|
|
10
|
+
//#region src/init/test/action.ts
|
|
11
|
+
const runTestInit = (options) => pipe(options, set("runId", genRunId), set("testDirPrefix", genTestDirPrefix), tap(emptyTestDir), fillEmptyOptions, tap(when(isHydRun, runTests(false))), tap(when(isDryRun, runTests(true))), tap(logTestDir));
|
|
12
|
+
var action_default = runTestInit;
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { action_default as default };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { CommandError, printErrorMessage, printMessage, product, runSubCommand } from "../../utils.js";
|
|
5
|
+
import pm_default from "../json/pm.js";
|
|
6
|
+
import webframeworks_default from "../webframeworks.js";
|
|
7
|
+
import { kvStores, messageQueues } from "../lib.js";
|
|
8
|
+
import { join, sep } from "node:path";
|
|
9
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
10
|
+
import { values } from "@optique/core";
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
import { filter, isEmpty, pipe, toArray } from "@fxts/core";
|
|
13
|
+
|
|
14
|
+
//#region src/init/test/create.ts
|
|
15
|
+
const BANNED_PMS = ["bun", "yarn"];
|
|
16
|
+
const createTestApp = (testDirPrefix, dry) => async (options) => {
|
|
17
|
+
const testDir = join(testDirPrefix, ...options);
|
|
18
|
+
const vals = values(testDir.split(sep).slice(-4));
|
|
19
|
+
try {
|
|
20
|
+
const result = await runSubCommand(toArray(genInitCommand(testDir, dry, options)), {
|
|
21
|
+
cwd: join(import.meta.dirname, "../../.."),
|
|
22
|
+
stdio: [
|
|
23
|
+
"ignore",
|
|
24
|
+
"pipe",
|
|
25
|
+
"pipe"
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
await saveOutputs(testDir, result);
|
|
29
|
+
printMessage` Pass: ${vals}`;
|
|
30
|
+
return testDir;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (error instanceof CommandError) await saveOutputs(testDir, {
|
|
33
|
+
stdout: error.stdout,
|
|
34
|
+
stderr: error.stderr
|
|
35
|
+
});
|
|
36
|
+
else {
|
|
37
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
38
|
+
await saveOutputs(testDir, {
|
|
39
|
+
stdout: "",
|
|
40
|
+
stderr: errorMessage
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
printMessage` Fail: ${vals}`;
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var create_default = createTestApp;
|
|
48
|
+
function* genInitCommand(testDir, dry, [webFramework, packageManager, kvStore, messageQueue]) {
|
|
49
|
+
yield "deno";
|
|
50
|
+
yield "run";
|
|
51
|
+
yield "-A";
|
|
52
|
+
yield "src/mod.ts";
|
|
53
|
+
yield "init";
|
|
54
|
+
yield testDir;
|
|
55
|
+
yield "-w";
|
|
56
|
+
yield webFramework;
|
|
57
|
+
yield "-p";
|
|
58
|
+
yield packageManager;
|
|
59
|
+
yield "-k";
|
|
60
|
+
yield kvStore;
|
|
61
|
+
yield "-m";
|
|
62
|
+
yield messageQueue;
|
|
63
|
+
yield "--test-mode";
|
|
64
|
+
if (dry) yield "-d";
|
|
65
|
+
}
|
|
66
|
+
const generateTestCases = ({ webFramework, packageManager, kvStore, messageQueue }) => {
|
|
67
|
+
const pms = filterPackageManager(packageManager);
|
|
68
|
+
exitIfCasesEmpty([
|
|
69
|
+
webFramework,
|
|
70
|
+
pms,
|
|
71
|
+
kvStore,
|
|
72
|
+
messageQueue
|
|
73
|
+
]);
|
|
74
|
+
return product(webFramework, pms, kvStore, messageQueue);
|
|
75
|
+
};
|
|
76
|
+
const filterPackageManager = (pm) => pipe(pm, filter((pm$1) => BANNED_PMS.includes(pm$1) ? printErrorMessage`${pm_default[pm$1]["label"]} is not \
|
|
77
|
+
supported in test mode yet because ${pm_default[pm$1]["label"]} don't \
|
|
78
|
+
support local file dependencies properly.` : true), toArray);
|
|
79
|
+
const exitIfCasesEmpty = (cases) => {
|
|
80
|
+
if (cases.some(isEmpty)) {
|
|
81
|
+
printErrorMessage`No test cases to run. Exiting.`;
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const saveOutputs = async (dirPath, { stdout, stderr }) => {
|
|
86
|
+
await mkdir(dirPath, { recursive: true });
|
|
87
|
+
if (stdout) await appendFile(join(dirPath, "out.txt"), stdout + "\n", "utf8");
|
|
88
|
+
if (stderr) await appendFile(join(dirPath, "err.txt"), stderr + "\n", "utf8");
|
|
89
|
+
};
|
|
90
|
+
function filterOptions(options) {
|
|
91
|
+
const [wf, pm, kv, mq] = options;
|
|
92
|
+
return [
|
|
93
|
+
webframeworks_default[wf].packageManagers,
|
|
94
|
+
kvStores[kv].packageManagers,
|
|
95
|
+
messageQueues[mq].packageManagers
|
|
96
|
+
].every((pms) => pms.includes(pm));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { create_default as default, filterOptions, generateTestCases };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK } from "../const.js";
|
|
5
|
+
import { isEmpty, pipe } from "@fxts/core";
|
|
6
|
+
|
|
7
|
+
//#region src/init/test/fill.ts
|
|
8
|
+
const fillEmptyOptions = (options) => pipe(options, fillWebFramework, fillPackageManager, fillKVStore, fillMessageQueue, fillRunMode);
|
|
9
|
+
const fillOption = (key, allValues) => (options) => ({
|
|
10
|
+
...options,
|
|
11
|
+
[key]: isEmpty(options[key]) ? allValues : options[key].filter((i) => allValues.includes(i))
|
|
12
|
+
});
|
|
13
|
+
const fillWebFramework = fillOption("webFramework", WEB_FRAMEWORK);
|
|
14
|
+
const fillPackageManager = fillOption("packageManager", PACKAGE_MANAGER);
|
|
15
|
+
const fillKVStore = fillOption("kvStore", KV_STORE);
|
|
16
|
+
const fillMessageQueue = fillOption("messageQueue", MESSAGE_QUEUE);
|
|
17
|
+
const fillRunMode = (options) => ({
|
|
18
|
+
...options,
|
|
19
|
+
...options.hydRun || options.dryRun ? {} : {
|
|
20
|
+
hydRun: true,
|
|
21
|
+
dryRun: true
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { fillEmptyOptions };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { printErrorMessage, printMessage, runSubCommand } from "../../utils.js";
|
|
5
|
+
import webframeworks_default from "../webframeworks.js";
|
|
6
|
+
import { getDevCommand } from "../lib.js";
|
|
7
|
+
import { join, sep } from "node:path";
|
|
8
|
+
import { values } from "@optique/core";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { createWriteStream } from "node:fs";
|
|
12
|
+
|
|
13
|
+
//#region src/init/test/lookup.ts
|
|
14
|
+
const HANDLE = "john";
|
|
15
|
+
const STARTUP_TIMEOUT = 3e4;
|
|
16
|
+
const CWD = process.cwd();
|
|
17
|
+
const BANNED_WFS = ["next"];
|
|
18
|
+
/**
|
|
19
|
+
* Run servers for all generated apps and test them with the lookup command.
|
|
20
|
+
*
|
|
21
|
+
* @param dirs - Array of paths to generated app directories
|
|
22
|
+
*/
|
|
23
|
+
async function runServerAndReadUser(dirs) {
|
|
24
|
+
const valid = dirs.filter(Boolean);
|
|
25
|
+
if (valid.length === 0) {
|
|
26
|
+
printErrorMessage`\nNo directories to lookup test.`;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const filtered = filterWebFrameworks(valid);
|
|
30
|
+
printMessage`\nLookup Test start for ${String(filtered.length)} app(s)!`;
|
|
31
|
+
const results = await Array.fromAsync(filtered, testApp);
|
|
32
|
+
const successCount = results.filter(Boolean).length;
|
|
33
|
+
const failCount = results.length - successCount;
|
|
34
|
+
printMessage`Lookup Test Results:
|
|
35
|
+
Total: ${String(results.length)}
|
|
36
|
+
Passed: ${String(successCount)}
|
|
37
|
+
Failed: ${String(failCount)}\n\n`;
|
|
38
|
+
}
|
|
39
|
+
function filterWebFrameworks(dirs) {
|
|
40
|
+
const wfs = new Set(dirs.map((dir) => dir.split(sep).slice(-4, -3)[0]));
|
|
41
|
+
const hasBanned = BANNED_WFS.filter((wf) => wfs.has(wf));
|
|
42
|
+
if (!hasBanned) return dirs;
|
|
43
|
+
const bannedLabels = hasBanned.map((wf) => webframeworks_default[wf]["label"]);
|
|
44
|
+
printErrorMessage`\n${values(bannedLabels)} is not supported in test mode yet.`;
|
|
45
|
+
return dirs.filter((dir) => !BANNED_WFS.includes(dir.split(sep).slice(-4, -3)[0]));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run the dev server and test with lookup command.
|
|
49
|
+
*/
|
|
50
|
+
async function testApp(dir) {
|
|
51
|
+
const [wf, pm, kv, mq] = dir.split(sep).slice(-4);
|
|
52
|
+
printMessage` Testing ${values([
|
|
53
|
+
wf,
|
|
54
|
+
pm,
|
|
55
|
+
kv,
|
|
56
|
+
mq
|
|
57
|
+
])}...`;
|
|
58
|
+
const result = await serverClosure(dir, getDevCommand(pm), sendLookup);
|
|
59
|
+
printMessage` Lookup ${result ? "successful" : "failed"} for ${values([
|
|
60
|
+
wf,
|
|
61
|
+
pm,
|
|
62
|
+
kv,
|
|
63
|
+
mq
|
|
64
|
+
])}!`;
|
|
65
|
+
if (!result) printMessage` Check out these files for more details:
|
|
66
|
+
${join(dir, "out.txt")}
|
|
67
|
+
${join(dir, "err.txt")}\n`;
|
|
68
|
+
printMessage`\n`;
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const sendLookup = async (port) => {
|
|
72
|
+
const serverUrl = `http://localhost:${port}`;
|
|
73
|
+
const lookupTarget = `${serverUrl}/users/${HANDLE}`;
|
|
74
|
+
printMessage` Waiting for server to start at ${serverUrl}...`;
|
|
75
|
+
const isReady = await waitForServer(serverUrl, STARTUP_TIMEOUT);
|
|
76
|
+
if (!isReady) {
|
|
77
|
+
printErrorMessage`Server did not start within \
|
|
78
|
+
${String(STARTUP_TIMEOUT)}ms`;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
printMessage` Server is ready. Running lookup command...`;
|
|
82
|
+
try {
|
|
83
|
+
await runSubCommand([
|
|
84
|
+
"deno",
|
|
85
|
+
"task",
|
|
86
|
+
"cli",
|
|
87
|
+
"lookup",
|
|
88
|
+
lookupTarget
|
|
89
|
+
], { cwd: CWD });
|
|
90
|
+
return true;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof Error) printErrorMessage`${error.message}`;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Wait for the server to be ready by checking if it responds to requests.
|
|
98
|
+
*/
|
|
99
|
+
async function waitForServer(url, timeout) {
|
|
100
|
+
const startTime = Date.now();
|
|
101
|
+
while (Date.now() - startTime < timeout) {
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(1e3) });
|
|
104
|
+
if (response.ok) return true;
|
|
105
|
+
} catch {}
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
async function serverClosure(dir, cmd, callback) {
|
|
111
|
+
const devCommand = cmd.split(" ");
|
|
112
|
+
const serverProcess = spawn(devCommand[0], devCommand.slice(1), {
|
|
113
|
+
cwd: dir,
|
|
114
|
+
stdio: [
|
|
115
|
+
"ignore",
|
|
116
|
+
"pipe",
|
|
117
|
+
"pipe"
|
|
118
|
+
],
|
|
119
|
+
detached: true
|
|
120
|
+
});
|
|
121
|
+
const stdout = createWriteStream(join(dir, "out.txt"), { flags: "a" });
|
|
122
|
+
const stderr = createWriteStream(join(dir, "err.txt"), { flags: "a" });
|
|
123
|
+
serverProcess.stdout?.pipe(stdout);
|
|
124
|
+
serverProcess.stderr?.pipe(stderr);
|
|
125
|
+
try {
|
|
126
|
+
const port = await determinePort(serverProcess);
|
|
127
|
+
return await callback(port);
|
|
128
|
+
} finally {
|
|
129
|
+
try {
|
|
130
|
+
process.kill(-serverProcess.pid, "SIGKILL");
|
|
131
|
+
} catch {
|
|
132
|
+
serverProcess.kill("SIGKILL");
|
|
133
|
+
stdout.end();
|
|
134
|
+
stderr.end();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function determinePort(server) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const timeout = setTimeout(() => {
|
|
141
|
+
reject(/* @__PURE__ */ new Error("Timeout: Could not determine port from server output"));
|
|
142
|
+
}, STARTUP_TIMEOUT);
|
|
143
|
+
let stdoutData = "";
|
|
144
|
+
let stderrData = "";
|
|
145
|
+
const portPatterns = [
|
|
146
|
+
/listening on.*:(\d+)/i,
|
|
147
|
+
/server.*:(\d+)/i,
|
|
148
|
+
/port\s*:?\s*(\d+)/i,
|
|
149
|
+
/localhost:(\d+)/i,
|
|
150
|
+
/0\.0\.0\.0:(\d+)/i,
|
|
151
|
+
/127\.0\.0\.1:(\d+)/i,
|
|
152
|
+
/https?:\/\/[^:]+:(\d+)/i
|
|
153
|
+
];
|
|
154
|
+
const checkForPort = (data) => {
|
|
155
|
+
for (const pattern of portPatterns) {
|
|
156
|
+
const match = data.match(pattern);
|
|
157
|
+
if (match && match[1]) {
|
|
158
|
+
const port = Number.parseInt(match[1], 10);
|
|
159
|
+
if (port > 0 && port < 65536) {
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
return port;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
167
|
+
server.stdout.on("data", (chunk) => {
|
|
168
|
+
stdoutData += chunk.toString();
|
|
169
|
+
const port = checkForPort(stdoutData);
|
|
170
|
+
if (port) resolve(port);
|
|
171
|
+
});
|
|
172
|
+
server.stderr.on("data", (chunk) => {
|
|
173
|
+
stderrData += chunk.toString();
|
|
174
|
+
const port = checkForPort(stderrData);
|
|
175
|
+
if (port) resolve(port);
|
|
176
|
+
});
|
|
177
|
+
server.on("error", (err) => {
|
|
178
|
+
clearTimeout(timeout);
|
|
179
|
+
reject(err);
|
|
180
|
+
});
|
|
181
|
+
server.on("exit", (code) => {
|
|
182
|
+
clearTimeout(timeout);
|
|
183
|
+
reject(/* @__PURE__ */ new Error(`Server exited with code ${code} before port could be determined`));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
export { runServerAndReadUser };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { printMessage } from "../../utils.js";
|
|
5
|
+
import create_default, { filterOptions, generateTestCases } from "./create.js";
|
|
6
|
+
import { runServerAndReadUser } from "./lookup.js";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { optionNames } from "@optique/core";
|
|
9
|
+
import { filter, map as map$1, pipe, tap } from "@fxts/core";
|
|
10
|
+
|
|
11
|
+
//#region src/init/test/run.ts
|
|
12
|
+
const isDryRun = ({ dryRun }) => dryRun;
|
|
13
|
+
const isHydRun = ({ hydRun }) => hydRun;
|
|
14
|
+
const runTests = (dry) => ({ testDirPrefix, dryRun, hydRun,...options }) => pipe(options, printStartMessage, generateTestCases, filter(filterOptions), map$1(create_default(join(testDirPrefix, getMid(dryRun, hydRun, dry)), dry)), Array.fromAsync, runServerAndReadUser);
|
|
15
|
+
const printStartMessage = tap(() => printMessage`\n
|
|
16
|
+
Init Test start!
|
|
17
|
+
Options: ${optionNames([
|
|
18
|
+
"Web Framework",
|
|
19
|
+
"Package Manager",
|
|
20
|
+
"KV Store",
|
|
21
|
+
"Message Queue"
|
|
22
|
+
])}`);
|
|
23
|
+
const getMid = (dryRun, hydRun, dry) => dryRun === hydRun ? dry ? "dry" : "hyd" : "";
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { isDryRun, isHydRun, runTests };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { printMessage } from "../../utils.js";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
|
|
8
|
+
//#region src/init/test/utils.ts
|
|
9
|
+
const genRunId = () => `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
10
|
+
const genTestDirPrefix = ({ runId }) => join(tmpdir(), "fedify-init", runId);
|
|
11
|
+
const emptyTestDir = ({ testDirPrefix }) => Deno.remove(testDirPrefix, { recursive: true }).catch(() => {});
|
|
12
|
+
const logTestDir = ({ runId, testDirPrefix }) => printMessage`Test run completed.
|
|
13
|
+
Run ID: ${runId}
|
|
14
|
+
Path: ${testDirPrefix}`;
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { emptyTestDir, genRunId, genTestDirPrefix, logTestDir };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { replace } from "../utils.js";
|
|
5
5
|
import { PACKAGE_MANAGER } from "./const.js";
|
|
6
|
-
import { PACKAGE_VERSION, getInstruction, getNextInitCommand, getNitroInitCommand, readTemplate } from "./lib.js";
|
|
6
|
+
import { PACKAGE_VERSION, getInstruction, getNextInitCommand, getNitroInitCommand, packageManagerToRuntime, readTemplate } from "./lib.js";
|
|
7
7
|
import { pipe } from "@fxts/core";
|
|
8
8
|
|
|
9
9
|
//#region src/init/webframeworks.ts
|
|
@@ -11,27 +11,30 @@ const webFrameworks = {
|
|
|
11
11
|
hono: {
|
|
12
12
|
label: "Hono",
|
|
13
13
|
packageManagers: PACKAGE_MANAGER,
|
|
14
|
-
init: (projectName, pm) => ({
|
|
14
|
+
init: ({ projectName, packageManager: pm }) => ({
|
|
15
15
|
dependencies: pm === "deno" ? {
|
|
16
16
|
"@std/dotenv": "^0.225.2",
|
|
17
17
|
"@hono/hono": "^4.5.0",
|
|
18
|
-
"@hongminhee/x-forwarded-fetch": "^0.2.0"
|
|
18
|
+
"@hongminhee/x-forwarded-fetch": "^0.2.0",
|
|
19
|
+
"@fedify/hono": PACKAGE_VERSION
|
|
19
20
|
} : pm === "bun" ? {
|
|
20
21
|
hono: "^4.5.0",
|
|
21
|
-
"x-forwarded-fetch": "^0.2.0"
|
|
22
|
+
"x-forwarded-fetch": "^0.2.0",
|
|
23
|
+
"@fedify/hono": PACKAGE_VERSION
|
|
22
24
|
} : {
|
|
23
25
|
"@dotenvx/dotenvx": "^1.14.1",
|
|
24
26
|
hono: "^4.5.0",
|
|
25
27
|
"@hono/node-server": "^1.12.0",
|
|
26
28
|
tsx: "^4.17.0",
|
|
27
|
-
"x-forwarded-fetch": "^0.2.0"
|
|
29
|
+
"x-forwarded-fetch": "^0.2.0",
|
|
30
|
+
"@fedify/hono": PACKAGE_VERSION
|
|
28
31
|
},
|
|
29
32
|
devDependencies: pm === "bun" ? { "@types/bun": "^1.1.6" } : {},
|
|
30
33
|
federationFile: "src/federation.ts",
|
|
31
34
|
loggingFile: "src/logging.ts",
|
|
32
35
|
files: {
|
|
33
|
-
"src/app.tsx": pipe("hono/app.tsx", readTemplate, replace(/\/\* hono \*\//, pm === "deno" ? "@hono/hono" : "hono")
|
|
34
|
-
"src/index.ts": readTemplate(`hono/index/${pm}.ts`)
|
|
36
|
+
"src/app.tsx": pipe("hono/app.tsx", readTemplate, replace(/\/\* hono \*\//, pm === "deno" ? "@hono/hono" : "hono"), replace(/\/\* logger \*\//, projectName)),
|
|
37
|
+
"src/index.ts": readTemplate(`hono/index/${packageManagerToRuntime(pm)}.ts`)
|
|
35
38
|
},
|
|
36
39
|
compilerOptions: pm === "deno" ? void 0 : {
|
|
37
40
|
"lib": ["ESNext", "DOM"],
|
|
@@ -49,20 +52,16 @@ const webFrameworks = {
|
|
|
49
52
|
"dev": pm === "deno" ? "deno run -A --watch ./src/index.ts" : pm === "bun" ? "bun run --hot ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
|
|
50
53
|
"prod": pm === "deno" ? "deno run -A ./src/index.ts" : pm === "bun" ? "bun run ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
|
|
51
54
|
},
|
|
52
|
-
instruction: getInstruction(pm)
|
|
53
|
-
})
|
|
55
|
+
instruction: getInstruction(pm, 8e3)
|
|
56
|
+
}),
|
|
57
|
+
defaultPort: 8e3
|
|
54
58
|
},
|
|
55
59
|
express: {
|
|
56
60
|
label: "Express",
|
|
57
|
-
packageManagers:
|
|
58
|
-
|
|
59
|
-
"npm",
|
|
60
|
-
"yarn",
|
|
61
|
-
"pnpm"
|
|
62
|
-
],
|
|
63
|
-
init: (projectName, pm) => ({
|
|
61
|
+
packageManagers: PACKAGE_MANAGER,
|
|
62
|
+
init: ({ projectName, packageManager: pm }) => ({
|
|
64
63
|
dependencies: {
|
|
65
|
-
express: "^4.19.2",
|
|
64
|
+
"npm:express": "^4.19.2",
|
|
66
65
|
"@fedify/express": PACKAGE_VERSION,
|
|
67
66
|
...pm !== "deno" && pm !== "bun" ? {
|
|
68
67
|
"@dotenvx/dotenvx": "^1.14.1",
|
|
@@ -90,16 +89,17 @@ const webFrameworks = {
|
|
|
90
89
|
"strict": true
|
|
91
90
|
},
|
|
92
91
|
tasks: {
|
|
93
|
-
"dev": pm === "bun" ? "bun run --hot ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
|
|
94
|
-
"prod": pm === "bun" ? "bun run ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
|
|
92
|
+
"dev": pm === "bun" ? "bun run --hot ./src/index.ts" : pm === "deno" ? "deno run --allow-net --allow-env --allow-sys --watch ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
|
|
93
|
+
"prod": pm === "bun" ? "bun run ./src/index.ts" : pm === "deno" ? "deno run --allow-net --allow-env --allow-sys ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
|
|
95
94
|
},
|
|
96
|
-
instruction: getInstruction(pm)
|
|
97
|
-
})
|
|
95
|
+
instruction: getInstruction(pm, 8e3)
|
|
96
|
+
}),
|
|
97
|
+
defaultPort: 8e3
|
|
98
98
|
},
|
|
99
99
|
nitro: {
|
|
100
100
|
label: "Nitro",
|
|
101
101
|
packageManagers: PACKAGE_MANAGER,
|
|
102
|
-
init: (
|
|
102
|
+
init: ({ packageManager: pm, testMode }) => ({
|
|
103
103
|
command: getNitroInitCommand(pm),
|
|
104
104
|
dependencies: { "@fedify/h3": PACKAGE_VERSION },
|
|
105
105
|
federationFile: "server/federation.ts",
|
|
@@ -107,15 +107,17 @@ const webFrameworks = {
|
|
|
107
107
|
files: {
|
|
108
108
|
"server/middleware/federation.ts": readTemplate("nitro/server/middleware/federation.ts"),
|
|
109
109
|
"server/error.ts": readTemplate("nitro/server/error.ts"),
|
|
110
|
-
"nitro.config.ts": readTemplate("nitro/nitro.config.ts")
|
|
110
|
+
"nitro.config.ts": readTemplate("nitro/nitro.config.ts"),
|
|
111
|
+
...testMode ? { ".env": readTemplate("nitro/.env.test") } : {}
|
|
111
112
|
},
|
|
112
|
-
instruction: getInstruction(pm)
|
|
113
|
-
})
|
|
113
|
+
instruction: getInstruction(pm, 3e3)
|
|
114
|
+
}),
|
|
115
|
+
defaultPort: 3e3
|
|
114
116
|
},
|
|
115
117
|
next: {
|
|
116
118
|
label: "Next.js",
|
|
117
119
|
packageManagers: PACKAGE_MANAGER,
|
|
118
|
-
init: (
|
|
120
|
+
init: ({ packageManager: pm }) => ({
|
|
119
121
|
label: "Next.js",
|
|
120
122
|
command: getNextInitCommand(pm),
|
|
121
123
|
dependencies: { "@fedify/next": PACKAGE_VERSION },
|
|
@@ -123,8 +125,9 @@ const webFrameworks = {
|
|
|
123
125
|
federationFile: "federation/index.ts",
|
|
124
126
|
loggingFile: "logging.ts",
|
|
125
127
|
files: { "middleware.ts": readTemplate("next/middleware.ts") },
|
|
126
|
-
instruction: getInstruction(pm)
|
|
127
|
-
})
|
|
128
|
+
instruction: getInstruction(pm, 3e3)
|
|
129
|
+
}),
|
|
130
|
+
defaultPort: 3e3
|
|
128
131
|
}
|
|
129
132
|
};
|
|
130
133
|
var webframeworks_default = webFrameworks;
|