@fedify/cli 2.0.0-pr.469.1873 → 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/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 +8 -19
- package/dist/init/action/patch.js +34 -28
- 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 +16 -10
- 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 +35 -33
- package/dist/mod.js +4 -2
- package/dist/utils.js +75 -8
- package/package.json +5 -5
- 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 +12 -13
- package/src/init/action/patch.ts +49 -28
- 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 +24 -12
- 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 +3 -3
- package/src/init/webframeworks.ts +39 -23
- package/src/mod.ts +10 -1
- package/src/utils.ts +128 -10
package/src/init/action/patch.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { apply, entries,
|
|
1
|
+
import { always, apply, entries, map, pipe, pipeLazy, tap } from "@fxts/core";
|
|
2
2
|
import { toMerged } from "es-toolkit";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { formatJson, merge, set } from "../../utils.ts";
|
|
4
|
+
import { formatJson, merge, replaceAll, set } from "../../utils.ts";
|
|
5
5
|
import { createFile, throwUnlessNotExists } from "../lib.ts";
|
|
6
6
|
import type { InitCommandData } from "../types.ts";
|
|
7
7
|
import {
|
|
@@ -23,7 +23,8 @@ import { joinDir, stringifyEnvs } from "./utils.ts";
|
|
|
23
23
|
* Handles both dry-run mode (recommending files) and actual file creation.
|
|
24
24
|
* Orchestrates the entire file generation and writing process.
|
|
25
25
|
*
|
|
26
|
-
* @param data - The initialization command data containing project
|
|
26
|
+
* @param data - The initialization command data containing project
|
|
27
|
+
* configuration
|
|
27
28
|
* @returns A processed data object with files and JSONs ready for creation
|
|
28
29
|
*/
|
|
29
30
|
export const patchFiles = (data: InitCommandData) =>
|
|
@@ -44,8 +45,9 @@ export const recommendPatchFiles = (data: InitCommandData) =>
|
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
47
|
* Generates text-based files (TypeScript, environment files) for the project.
|
|
47
|
-
* Creates federation configuration, logging setup, environment variables, and
|
|
48
|
-
* by processing templates and combining them with
|
|
48
|
+
* Creates federation configuration, logging setup, environment variables, and
|
|
49
|
+
* framework-specific files by processing templates and combining them with
|
|
50
|
+
* project-specific data.
|
|
49
51
|
*
|
|
50
52
|
* @param data - The initialization command data
|
|
51
53
|
* @returns A record of file paths to their string content
|
|
@@ -53,7 +55,7 @@ export const recommendPatchFiles = (data: InitCommandData) =>
|
|
|
53
55
|
const getFiles = <
|
|
54
56
|
T extends InitCommandData,
|
|
55
57
|
>(data: T) => ({
|
|
56
|
-
[data.initializer.federationFile
|
|
58
|
+
[data.initializer.federationFile]: loadFederation({
|
|
57
59
|
imports: getImports(data),
|
|
58
60
|
...data,
|
|
59
61
|
}),
|
|
@@ -64,8 +66,9 @@ const getFiles = <
|
|
|
64
66
|
|
|
65
67
|
/**
|
|
66
68
|
* Generates JSON configuration files based on the package manager type.
|
|
67
|
-
* Creates different sets of configuration files for Deno vs
|
|
68
|
-
* including compiler configs, package manifests, and development
|
|
69
|
+
* Creates different sets of configuration files for Deno vs other environments,
|
|
70
|
+
* including compiler configs, package manifests, and development
|
|
71
|
+
* tool configurations.
|
|
69
72
|
*
|
|
70
73
|
* @param data - The initialization command data
|
|
71
74
|
* @returns A record of file paths to their JSON object content
|
|
@@ -80,7 +83,9 @@ const getJsons = <
|
|
|
80
83
|
[devToolConfigs["vscExtDeno"].path]: devToolConfigs["vscExtDeno"].data,
|
|
81
84
|
}
|
|
82
85
|
: {
|
|
83
|
-
|
|
86
|
+
...(data.initializer.compilerOptions
|
|
87
|
+
? { "tsconfig.json": loadTsConfig(data).data }
|
|
88
|
+
: {}),
|
|
84
89
|
"package.json": loadPackageJson(data).data,
|
|
85
90
|
[devToolConfigs["biome"].path]: devToolConfigs["biome"].data,
|
|
86
91
|
[devToolConfigs["vscSet"].path]: devToolConfigs["vscSet"].data,
|
|
@@ -88,9 +93,10 @@ const getJsons = <
|
|
|
88
93
|
};
|
|
89
94
|
|
|
90
95
|
/**
|
|
91
|
-
* Handles dry-run mode by recommending files to be created without actually
|
|
92
|
-
*
|
|
93
|
-
*
|
|
96
|
+
* Handles dry-run mode by recommending files to be created without actually
|
|
97
|
+
* creating them.
|
|
98
|
+
* Displays what files would be created and shows their content for user review,
|
|
99
|
+
* so users can preview the initialization process before committing to it.
|
|
94
100
|
*
|
|
95
101
|
* @param data - The initialization command data with files and JSONs prepared
|
|
96
102
|
* @returns The processed data with recommendations displayed
|
|
@@ -106,7 +112,7 @@ const recommendFiles = (data: InitCommandWithFiles) =>
|
|
|
106
112
|
);
|
|
107
113
|
|
|
108
114
|
/**
|
|
109
|
-
* Actually creates files on the filesystem during normal
|
|
115
|
+
* Actually creates files on the filesystem during normal execution.
|
|
110
116
|
* Merges text files and JSON files together and writes them to disk.
|
|
111
117
|
* This performs the actual file system operations to initialize the project.
|
|
112
118
|
*
|
|
@@ -126,8 +132,8 @@ interface InitCommandWithFiles extends InitCommandData {
|
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
/**
|
|
129
|
-
*
|
|
130
|
-
* Takes a
|
|
135
|
+
* Processes all files with a given processing function.
|
|
136
|
+
* Takes a processor (either display or create) and applies it to all files
|
|
131
137
|
* in the target directory, handling path resolution and content patching.
|
|
132
138
|
*
|
|
133
139
|
* @param process - Function to process each file (either display or create)
|
|
@@ -140,19 +146,20 @@ const processAllFiles = (
|
|
|
140
146
|
pipe(
|
|
141
147
|
files,
|
|
142
148
|
entries,
|
|
143
|
-
|
|
149
|
+
map(
|
|
144
150
|
pipeLazy(
|
|
145
151
|
joinDir(dir),
|
|
146
152
|
apply(patchContent),
|
|
147
153
|
apply(process),
|
|
148
154
|
),
|
|
149
155
|
),
|
|
156
|
+
Array.fromAsync,
|
|
150
157
|
);
|
|
151
158
|
|
|
152
159
|
/**
|
|
153
|
-
* Patches file content by either merging JSON
|
|
154
|
-
* Handles existing files by reading their current content and intelligently
|
|
155
|
-
* it with new content based on the content type (JSON vs text).
|
|
160
|
+
* Patches file content by either merging JSON or appending text content.
|
|
161
|
+
* Handles existing files by reading their current content and intelligently
|
|
162
|
+
* combining it with new content based on the content type (JSON vs text).
|
|
156
163
|
*
|
|
157
164
|
* @param path - The file path to patch
|
|
158
165
|
* @param content - The new content (either string or object)
|
|
@@ -173,13 +180,32 @@ async function patchContent(
|
|
|
173
180
|
* Merges new JSON data with existing JSON content and formats the result.
|
|
174
181
|
* Parses existing JSON content (if any) and deep merges it with new data,
|
|
175
182
|
* then formats the result for consistent output.
|
|
183
|
+
* Supports JSONC (JSON with Comments) by removing comments before parsing.
|
|
176
184
|
*
|
|
177
185
|
* @param prev - The previous JSON content as string
|
|
178
186
|
* @param data - The new data object to merge
|
|
179
187
|
* @returns Formatted JSON string with merged content
|
|
180
188
|
*/
|
|
181
189
|
const mergeJson = (prev: string, data: object): string =>
|
|
182
|
-
pipe(
|
|
190
|
+
pipe(
|
|
191
|
+
prev ? JSON.parse(removeJsonComments(prev)) : {},
|
|
192
|
+
merge(data),
|
|
193
|
+
formatJson,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Removes single-line (//) and multi-line (/* *\/) comments from JSON string.
|
|
198
|
+
* This allows parsing JSONC (JSON with Comments) files.
|
|
199
|
+
*
|
|
200
|
+
* @param jsonString - The JSON string potentially containing comments
|
|
201
|
+
* @returns JSON string with comments removed
|
|
202
|
+
*/
|
|
203
|
+
const removeJsonComments = (jsonString: string): string =>
|
|
204
|
+
pipe(
|
|
205
|
+
jsonString,
|
|
206
|
+
replaceAll(/\/\/.*$/gm, ""),
|
|
207
|
+
replaceAll(/\/\*[\s\S]*?\*\//g, ""),
|
|
208
|
+
);
|
|
183
209
|
|
|
184
210
|
/**
|
|
185
211
|
* Appends new text content to existing text content line by line.
|
|
@@ -202,11 +228,6 @@ const appendText = (prev: string, data: string) =>
|
|
|
202
228
|
* @returns The file content as string, or empty string if file doesn't exist
|
|
203
229
|
* @throws Error if file access fails for reasons other than file not existing
|
|
204
230
|
*/
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
} catch (e) {
|
|
209
|
-
throwUnlessNotExists(e);
|
|
210
|
-
return "";
|
|
211
|
-
}
|
|
212
|
-
}
|
|
231
|
+
const readFileIfExists = (path: string): Promise<string> =>
|
|
232
|
+
readFile(path, "utf8")
|
|
233
|
+
.catch(pipeLazy(tap(throwUnlessNotExists), always("")));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exit, runSubCommand } from "../../utils.ts";
|
|
1
|
+
import { CommandError, exit, runSubCommand } from "../../utils.ts";
|
|
2
2
|
import type { InitCommandData } from "../types.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -15,7 +15,14 @@ const runPrecommand = ({
|
|
|
15
15
|
cwd: dir,
|
|
16
16
|
stdio: "inherit",
|
|
17
17
|
}).catch((e) => {
|
|
18
|
-
|
|
18
|
+
if (e instanceof CommandError) {
|
|
19
|
+
console.error("Failed to run the precommand.");
|
|
20
|
+
console.error("Command:", e.commandLine);
|
|
21
|
+
if (e.stderr) console.error("Error:", e.stderr);
|
|
22
|
+
if (e.stdout) console.error("Output:", e.stdout);
|
|
23
|
+
} else {
|
|
24
|
+
console.error("Failed to run the precommand:", e);
|
|
25
|
+
}
|
|
19
26
|
exit(1);
|
|
20
27
|
});
|
|
21
28
|
|
package/src/init/action/set.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
KvStoreDescription,
|
|
12
12
|
MessageQueue,
|
|
13
13
|
MessageQueueDescription,
|
|
14
|
-
PackageManager,
|
|
15
14
|
} from "../types.ts";
|
|
16
15
|
import webFrameworks from "../webframeworks.ts";
|
|
17
16
|
|
|
@@ -45,20 +44,8 @@ const setProjectName = set(
|
|
|
45
44
|
);
|
|
46
45
|
|
|
47
46
|
const setInitializer = set("initializer", <
|
|
48
|
-
T extends {
|
|
49
|
-
|
|
50
|
-
projectName: string;
|
|
51
|
-
packageManager: PackageManager;
|
|
52
|
-
},
|
|
53
|
-
>({
|
|
54
|
-
webFramework,
|
|
55
|
-
projectName,
|
|
56
|
-
packageManager,
|
|
57
|
-
}: T) =>
|
|
58
|
-
webFrameworks[webFramework].init(
|
|
59
|
-
projectName,
|
|
60
|
-
packageManager,
|
|
61
|
-
));
|
|
47
|
+
T extends InitCommandOptions & { projectName: string },
|
|
48
|
+
>(data: T) => webFrameworks[data.webFramework].init(data));
|
|
62
49
|
|
|
63
50
|
const setKv = set("kv", <
|
|
64
51
|
T extends { kvStore: KvStore },
|
|
@@ -81,6 +81,7 @@ export const getAlias = (imports: Record<string, string>) =>
|
|
|
81
81
|
join(", "),
|
|
82
82
|
);
|
|
83
83
|
|
|
84
|
+
const ENV_REG_EXP = /process\.env\.(\w+)/g;
|
|
84
85
|
/**
|
|
85
86
|
* Converts Node.js environment variable access to Deno-compatible syntax when needed.
|
|
86
87
|
* Transforms `process.env.VAR_NAME` to `Deno.env.get("VAR_NAME")` for Deno projects.
|
|
@@ -90,6 +91,6 @@ export const getAlias = (imports: Record<string, string>) =>
|
|
|
90
91
|
* @returns The converted object string with appropriate environment variable access syntax
|
|
91
92
|
*/
|
|
92
93
|
export const convertEnv = (obj: string, pm: PackageManager) =>
|
|
93
|
-
pm === "deno" &&
|
|
94
|
-
? obj.replaceAll(
|
|
94
|
+
pm === "deno" && ENV_REG_EXP.test(obj)
|
|
95
|
+
? obj.replaceAll(ENV_REG_EXP, (_, g1) => `Deno.env.get("${g1}")`)
|
|
95
96
|
: obj;
|
package/src/init/action/utils.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const isDry = ({ dryRun }: InitCommandData) => dryRun;
|
|
|
6
6
|
export const hasCommand = (data: InitCommandData) => !!data.initializer.command;
|
|
7
7
|
|
|
8
8
|
export const isDeno = (
|
|
9
|
-
{ packageManager }: InitCommandData,
|
|
9
|
+
{ packageManager }: Pick<InitCommandData, "packageManager">,
|
|
10
10
|
) => packageManager === "deno";
|
|
11
11
|
|
|
12
12
|
export const joinDir =
|
package/src/init/ask/kv.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js";
|
|
1
2
|
import { select } from "@inquirer/prompts";
|
|
3
|
+
import { printErrorMessage } from "../../utils.ts";
|
|
2
4
|
import { KV_STORE } from "../const.ts";
|
|
3
|
-
import { kvStores } from "../lib.ts";
|
|
5
|
+
import { isTest, kvStores } from "../lib.ts";
|
|
4
6
|
import type { KvStore, PackageManager } from "../types.ts";
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -11,29 +13,70 @@ import type { KvStore, PackageManager } from "../types.ts";
|
|
|
11
13
|
* @returns A promise resolving to options with a guaranteed kvStore
|
|
12
14
|
*/
|
|
13
15
|
const fillKvStore: //
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<
|
|
17
|
+
T extends {
|
|
18
|
+
kvStore?: KvStore;
|
|
19
|
+
packageManager: PackageManager;
|
|
20
|
+
testMode: boolean;
|
|
21
|
+
},
|
|
22
|
+
>(options: T) => Promise<KvDefined<T>> //
|
|
23
|
+
= (options) =>
|
|
24
|
+
pipe(
|
|
25
|
+
options,
|
|
26
|
+
when(isKvStoreEmpty, askKvStore) as <
|
|
27
|
+
T extends { kvStore?: KvStore; packageManager: PackageManager },
|
|
28
|
+
>(options: T) => KvDefined<T>,
|
|
29
|
+
unless(
|
|
30
|
+
isKvSupportsPm,
|
|
31
|
+
(opt: KvDefined<typeof options>) =>
|
|
32
|
+
pipe(
|
|
33
|
+
opt,
|
|
34
|
+
when(isTest, throwError(unmatchedWhileTesting)),
|
|
35
|
+
tap(noticeUnmatched),
|
|
36
|
+
fillKvStore,
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
) as Promise<KvDefined<typeof options>>;
|
|
21
40
|
|
|
22
41
|
export default fillKvStore;
|
|
23
42
|
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
type KvDefined<T> = Omit<T, "kvStore"> & { kvStore: KvStore };
|
|
44
|
+
|
|
45
|
+
const isKvStoreEmpty = <T extends { kvStore?: KvStore }>(
|
|
46
|
+
options: T,
|
|
47
|
+
): options is T & { kvStore: undefined } => !options.kvStore;
|
|
48
|
+
|
|
49
|
+
const askKvStore = async <
|
|
50
|
+
T extends { packageManager: PackageManager },
|
|
51
|
+
>(data: T): Promise<Omit<T, "kvStore"> & { kvStore: KvStore }> => ({
|
|
52
|
+
...data,
|
|
53
|
+
kvStore: await select<KvStore>({
|
|
26
54
|
message: "Choose the key-value store to use",
|
|
27
|
-
choices: KV_STORE.map(choiceKvStore(
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
choices: KV_STORE.map(choiceKvStore(data.packageManager)),
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const unmatchedWhileTesting = <
|
|
60
|
+
T extends { kvStore: KvStore; packageManager: PackageManager },
|
|
61
|
+
>({ kvStore: kv, packageManager: pm }: T) =>
|
|
62
|
+
new Error(
|
|
63
|
+
`Key-value store '${kv}' is not compatible with package manager '${pm}'`,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const noticeUnmatched = <
|
|
67
|
+
T extends { kvStore: KvStore; packageManager: PackageManager },
|
|
68
|
+
>({ kvStore: kv, packageManager: pm }: T) =>
|
|
69
|
+
printErrorMessage`Error: Key-value store '${kv}' is not compatible with package manager '${pm}'`;
|
|
70
|
+
|
|
71
|
+
const choiceKvStore = (pm: PackageManager) => (kv: KvStore) => ({
|
|
72
|
+
name: isKvSupportsPm({ kvStore: kv, packageManager: pm })
|
|
73
|
+
? kv
|
|
74
|
+
: `${kv} (not supported with ${pm})`,
|
|
75
|
+
value: kv,
|
|
76
|
+
disabled: !isKvSupportsPm({ kvStore: kv, packageManager: pm }),
|
|
36
77
|
});
|
|
37
78
|
|
|
38
|
-
const isKvSupportsPm =
|
|
39
|
-
|
|
79
|
+
const isKvSupportsPm = <
|
|
80
|
+
T extends { kvStore: KvStore; packageManager: PackageManager },
|
|
81
|
+
>({ kvStore, packageManager }: T) =>
|
|
82
|
+
kvStores[kvStore].packageManagers.includes(packageManager);
|
package/src/init/ask/mq.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js";
|
|
1
2
|
import { select } from "@inquirer/prompts";
|
|
3
|
+
import { printErrorMessage } from "../../utils.ts";
|
|
2
4
|
import { MESSAGE_QUEUE } from "../const.ts";
|
|
3
|
-
import { messageQueues } from "../lib.ts";
|
|
5
|
+
import { isTest, messageQueues } from "../lib.ts";
|
|
4
6
|
import type { MessageQueue, PackageManager } from "../types.ts";
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -11,27 +13,74 @@ import type { MessageQueue, PackageManager } from "../types.ts";
|
|
|
11
13
|
* @returns A promise resolving to options with a guaranteed messageQueue
|
|
12
14
|
*/
|
|
13
15
|
const fillMessageQueue: //
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<
|
|
17
|
+
T extends {
|
|
18
|
+
messageQueue?: MessageQueue;
|
|
19
|
+
packageManager: PackageManager;
|
|
20
|
+
testMode: boolean;
|
|
21
|
+
},
|
|
22
|
+
>(options: T) => Promise<MqDefined<T>> //
|
|
23
|
+
= (options) =>
|
|
24
|
+
pipe(
|
|
25
|
+
options,
|
|
26
|
+
when(isMessageQueueEmpty, askMessageQueue) as <
|
|
27
|
+
T extends { messageQueue?: MessageQueue; packageManager: PackageManager },
|
|
28
|
+
>(options: T) => MqDefined<T>,
|
|
29
|
+
unless(
|
|
30
|
+
isMqSupportsPm,
|
|
31
|
+
(opt: MqDefined<typeof options>) =>
|
|
32
|
+
pipe(
|
|
33
|
+
opt,
|
|
34
|
+
when(isTest, throwError(unmatchedWhileTesting)),
|
|
35
|
+
tap(noticeUnmatched),
|
|
36
|
+
fillMessageQueue,
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
) as Promise<MqDefined<typeof options>>;
|
|
21
40
|
|
|
22
41
|
export default fillMessageQueue;
|
|
23
42
|
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
type MqDefined<T> = Omit<T, "messageQueue"> & { messageQueue: MessageQueue };
|
|
44
|
+
|
|
45
|
+
const isMessageQueueEmpty = <T extends { messageQueue?: MessageQueue }>(
|
|
46
|
+
options: T,
|
|
47
|
+
): options is T & { messageQueue: undefined } => !options.messageQueue;
|
|
48
|
+
|
|
49
|
+
const askMessageQueue = async <
|
|
50
|
+
T extends { packageManager: PackageManager },
|
|
51
|
+
>(
|
|
52
|
+
data: T,
|
|
53
|
+
): Promise<Omit<T, "messageQueue"> & { messageQueue: MessageQueue }> => ({
|
|
54
|
+
...data,
|
|
55
|
+
messageQueue: await select<MessageQueue>({
|
|
26
56
|
message: "Choose the message queue to use",
|
|
27
|
-
choices: MESSAGE_QUEUE.map(choiceMessageQueue(
|
|
28
|
-
})
|
|
29
|
-
const choiceMessageQueue = (pm: PackageManager) => (value: MessageQueue) => ({
|
|
30
|
-
name: isMqSupportsPm(value, pm)
|
|
31
|
-
? value
|
|
32
|
-
: `${value} (not supported with ${pm})`,
|
|
33
|
-
value,
|
|
34
|
-
disabled: !isMqSupportsPm(value, pm),
|
|
57
|
+
choices: MESSAGE_QUEUE.map(choiceMessageQueue(data.packageManager)),
|
|
58
|
+
}),
|
|
35
59
|
});
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
|
|
61
|
+
const unmatchedWhileTesting = <
|
|
62
|
+
T extends { messageQueue: MessageQueue; packageManager: PackageManager },
|
|
63
|
+
>({ messageQueue: mq, packageManager: pm }: T) =>
|
|
64
|
+
new Error(
|
|
65
|
+
`Message queue '${mq}' is not compatible with package manager '${pm}'`,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const noticeUnmatched = <
|
|
69
|
+
T extends { messageQueue: MessageQueue; packageManager: PackageManager },
|
|
70
|
+
>({ messageQueue: mq, packageManager: pm }: T) => {
|
|
71
|
+
printErrorMessage`Error: Message queue '${mq}' is not compatible with package manager '${pm}'`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const choiceMessageQueue =
|
|
75
|
+
(packageManager: PackageManager) => (messageQueue: MessageQueue) => ({
|
|
76
|
+
name: isMqSupportsPm({ messageQueue, packageManager })
|
|
77
|
+
? messageQueue
|
|
78
|
+
: `${messageQueue} (not supported with ${packageManager})`,
|
|
79
|
+
value: messageQueue,
|
|
80
|
+
disabled: !isMqSupportsPm({ messageQueue, packageManager }),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const isMqSupportsPm = <
|
|
84
|
+
T extends { messageQueue: MessageQueue; packageManager: PackageManager },
|
|
85
|
+
>({ messageQueue, packageManager }: T) =>
|
|
86
|
+
messageQueues[messageQueue].packageManagers.includes(packageManager);
|
package/src/init/command.ts
CHANGED
|
@@ -5,19 +5,20 @@ import {
|
|
|
5
5
|
constant,
|
|
6
6
|
type InferValue,
|
|
7
7
|
message,
|
|
8
|
+
multiple,
|
|
8
9
|
object,
|
|
9
10
|
option,
|
|
10
11
|
optional,
|
|
11
12
|
optionNames,
|
|
12
13
|
} from "@optique/core";
|
|
13
14
|
import { path } from "@optique/run";
|
|
15
|
+
import { debugOption } from "../globals.ts";
|
|
14
16
|
import {
|
|
15
17
|
KV_STORE,
|
|
16
18
|
MESSAGE_QUEUE,
|
|
17
19
|
PACKAGE_MANAGER,
|
|
18
20
|
WEB_FRAMEWORK,
|
|
19
21
|
} from "./const.ts";
|
|
20
|
-
import { debugOption } from "../globals.ts";
|
|
21
22
|
|
|
22
23
|
const webFramework = optional(option(
|
|
23
24
|
"-w",
|
|
@@ -53,6 +54,12 @@ const messageQueue = optional(option(
|
|
|
53
54
|
description: message`The message queue to use for background tasks.`,
|
|
54
55
|
},
|
|
55
56
|
));
|
|
57
|
+
const testMode = option(
|
|
58
|
+
"--test-mode",
|
|
59
|
+
{
|
|
60
|
+
description: message`The test mode to use for testing purposes.`,
|
|
61
|
+
},
|
|
62
|
+
);
|
|
56
63
|
|
|
57
64
|
export const initCommand = command(
|
|
58
65
|
"init",
|
|
@@ -70,6 +77,7 @@ export const initCommand = command(
|
|
|
70
77
|
description: message`Perform a trial run with no changes made.`,
|
|
71
78
|
}),
|
|
72
79
|
debugOption,
|
|
80
|
+
testMode,
|
|
73
81
|
}),
|
|
74
82
|
{
|
|
75
83
|
brief: message`Initialize a new Fedify project directory.`,
|
|
@@ -86,3 +94,33 @@ Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${
|
|
|
86
94
|
);
|
|
87
95
|
|
|
88
96
|
export type InitCommand = InferValue<typeof initCommand>;
|
|
97
|
+
|
|
98
|
+
export const testInitCommand = command(
|
|
99
|
+
"test-init",
|
|
100
|
+
object("Initialization options", {
|
|
101
|
+
command: constant("test-init"),
|
|
102
|
+
webFramework: multiple(webFramework),
|
|
103
|
+
packageManager: multiple(packageManager),
|
|
104
|
+
kvStore: multiple(kvStore),
|
|
105
|
+
messageQueue: multiple(messageQueue),
|
|
106
|
+
hydRun: option("-h", "--hyd-run", {
|
|
107
|
+
description: message`Test with files creations and installations.`,
|
|
108
|
+
}),
|
|
109
|
+
dryRun: option("-d", "--dry-run", {
|
|
110
|
+
description: message`Log outputs without creating files.`,
|
|
111
|
+
}),
|
|
112
|
+
debugOption,
|
|
113
|
+
}),
|
|
114
|
+
{
|
|
115
|
+
brief: message`Test an initializing command .`,
|
|
116
|
+
description: message`Test an initializing command on temporary directories.
|
|
117
|
+
|
|
118
|
+
Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${
|
|
119
|
+
optionNames(["-p", "--package-manager"])
|
|
120
|
+
}, ${optionNames(["-k", "--kv-store"])}, and ${
|
|
121
|
+
optionNames(["-m", "--message-queue"])
|
|
122
|
+
}), it will test all combinations of the options.`,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
export type TestInitCommand = InferValue<typeof testInitCommand>;
|
package/src/init/lib.ts
CHANGED
|
@@ -110,7 +110,8 @@ export const readTemplate: (templatePath: string) => string = (
|
|
|
110
110
|
|
|
111
111
|
export const getInstruction: (
|
|
112
112
|
packageManager: PackageManager,
|
|
113
|
-
|
|
113
|
+
port: number,
|
|
114
|
+
) => Message = (pm, port) =>
|
|
114
115
|
message`
|
|
115
116
|
To start the server, run the following command:
|
|
116
117
|
|
|
@@ -118,11 +119,11 @@ To start the server, run the following command:
|
|
|
118
119
|
|
|
119
120
|
Then, try look up an actor from your server:
|
|
120
121
|
|
|
121
|
-
${commandLine(
|
|
122
|
+
${commandLine(`fedify lookup http://localhost:${port}/users/john`)}
|
|
122
123
|
|
|
123
124
|
`;
|
|
124
125
|
|
|
125
|
-
const getDevCommand = (pm: PackageManager) =>
|
|
126
|
+
export const getDevCommand = (pm: PackageManager) =>
|
|
126
127
|
pm === "deno" ? "deno task dev" : pm === "bun" ? "bun dev" : `${pm} run dev`;
|
|
127
128
|
|
|
128
129
|
async function isCommandAvailable(
|
|
@@ -172,20 +173,23 @@ export const isDirectoryEmpty = async (
|
|
|
172
173
|
}
|
|
173
174
|
};
|
|
174
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Converts a package manager to its corresponding runtime.
|
|
178
|
+
* @param pm - The package manager (deno, bun, npm, yarn, pnpm)
|
|
179
|
+
* @returns The runtime name (deno, bun, or node)
|
|
180
|
+
*/
|
|
181
|
+
export const packageManagerToRuntime = (
|
|
182
|
+
pm: PackageManager,
|
|
183
|
+
): "deno" | "bun" | "node" =>
|
|
184
|
+
pm === "deno" ? "deno" : pm === "bun" ? "bun" : "node";
|
|
185
|
+
|
|
175
186
|
export const getNextInitCommand = (
|
|
176
187
|
pm: PackageManager,
|
|
177
|
-
): string[] => [
|
|
178
|
-
...createNextAppCommand(pm),
|
|
179
|
-
".",
|
|
180
|
-
"--ts",
|
|
181
|
-
"--app",
|
|
182
|
-
"--biome",
|
|
183
|
-
"--skip-install",
|
|
184
|
-
];
|
|
188
|
+
): string[] => [...createNextAppCommand(pm), ".", "--yes"];
|
|
185
189
|
|
|
186
190
|
const createNextAppCommand = (pm: PackageManager): string[] =>
|
|
187
191
|
pm === "deno"
|
|
188
|
-
? ["deno", "
|
|
192
|
+
? ["deno", "-Ar", "npm:create-next-app@latest"]
|
|
189
193
|
: pm === "bun"
|
|
190
194
|
? ["bun", "create", "next-app"]
|
|
191
195
|
: pm === "npm"
|
|
@@ -199,6 +203,10 @@ export const getNitroInitCommand = (
|
|
|
199
203
|
pm === "deno" ? "npm:giget@latest" : "giget@latest",
|
|
200
204
|
"nitro",
|
|
201
205
|
".",
|
|
206
|
+
"&&",
|
|
207
|
+
"rm",
|
|
208
|
+
"nitro.config.ts", // Remove default nitro config file
|
|
209
|
+
// This file will be created from template
|
|
202
210
|
];
|
|
203
211
|
|
|
204
212
|
const createNitroAppCommand = (pm: PackageManager): string[] =>
|
|
@@ -209,3 +217,7 @@ const createNitroAppCommand = (pm: PackageManager): string[] =>
|
|
|
209
217
|
: pm === "npm"
|
|
210
218
|
? ["npx"]
|
|
211
219
|
: [pm, "dlx"];
|
|
220
|
+
|
|
221
|
+
export const isTest: <
|
|
222
|
+
T extends { testMode: boolean },
|
|
223
|
+
>({ testMode }: T) => boolean = ({ testMode }) => testMode;
|
package/src/init/mod.ts
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,25 @@
|
|
|
1
|
+
import { pipe, tap, when } from "@fxts/core";
|
|
2
|
+
import { set } from "../../utils.ts";
|
|
3
|
+
import type { TestInitCommand } from "../command.ts";
|
|
4
|
+
import { fillEmptyOptions } from "./fill.ts";
|
|
5
|
+
import { isDryRun, isHydRun, runTests } from "./run.ts";
|
|
6
|
+
import {
|
|
7
|
+
emptyTestDir,
|
|
8
|
+
genRunId,
|
|
9
|
+
genTestDirPrefix,
|
|
10
|
+
logTestDir,
|
|
11
|
+
} from "./utils.ts";
|
|
12
|
+
|
|
13
|
+
const runTestInit = (options: TestInitCommand) =>
|
|
14
|
+
pipe(
|
|
15
|
+
options,
|
|
16
|
+
set("runId", genRunId),
|
|
17
|
+
set("testDirPrefix", genTestDirPrefix),
|
|
18
|
+
tap(emptyTestDir),
|
|
19
|
+
fillEmptyOptions,
|
|
20
|
+
tap(when(isHydRun, runTests(false))),
|
|
21
|
+
tap(when(isDryRun, runTests(true))),
|
|
22
|
+
tap(logTestDir),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export default runTestInit;
|