@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/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
|
|
@@ -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
|
@@ -10,13 +10,15 @@ import {
|
|
|
10
10
|
when,
|
|
11
11
|
} from "@fxts/core";
|
|
12
12
|
import { getLogger } from "@logtape/logtape";
|
|
13
|
-
import {
|
|
13
|
+
import type { Message } from "@optique/core";
|
|
14
|
+
import { commandLine, message } from "@optique/core/message";
|
|
14
15
|
import { toMerged } from "es-toolkit";
|
|
15
16
|
import { readFileSync } from "node:fs";
|
|
16
17
|
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
18
|
+
import { dirname, join as joinPath } from "node:path";
|
|
17
19
|
import process from "node:process";
|
|
18
20
|
import metadata from "../../deno.json" with { type: "json" };
|
|
19
|
-
import {
|
|
21
|
+
import { isNotFoundError, runSubCommand } from "../utils.ts";
|
|
20
22
|
import kv from "./json/kv.json" with { type: "json" };
|
|
21
23
|
import mq from "./json/mq.json" with { type: "json" };
|
|
22
24
|
import pm from "./json/pm.json" with { type: "json" };
|
|
@@ -108,31 +110,21 @@ export const readTemplate: (templatePath: string) => string = (
|
|
|
108
110
|
|
|
109
111
|
export const getInstruction: (
|
|
110
112
|
packageManager: PackageManager,
|
|
111
|
-
|
|
113
|
+
port: number,
|
|
114
|
+
) => Message = (pm, port) =>
|
|
115
|
+
message`
|
|
112
116
|
To start the server, run the following command:
|
|
113
117
|
|
|
114
|
-
${getDevCommand(pm)}
|
|
118
|
+
${commandLine(getDevCommand(pm))}
|
|
115
119
|
|
|
116
120
|
Then, try look up an actor from your server:
|
|
117
121
|
|
|
118
|
-
${
|
|
119
|
-
colors.bold(colors.green(
|
|
120
|
-
"fedify lookup http://localhost:8000/users/john",
|
|
121
|
-
))
|
|
122
|
-
}
|
|
122
|
+
${commandLine(`fedify lookup http://localhost:${port}/users/john`)}
|
|
123
123
|
|
|
124
124
|
`;
|
|
125
125
|
|
|
126
|
-
const getDevCommand = (pm: PackageManager) =>
|
|
127
|
-
|
|
128
|
-
colors.green(
|
|
129
|
-
pm === "deno"
|
|
130
|
-
? "deno task dev"
|
|
131
|
-
: pm === "bun"
|
|
132
|
-
? "bun dev"
|
|
133
|
-
: `${pm} run dev`,
|
|
134
|
-
),
|
|
135
|
-
);
|
|
126
|
+
export const getDevCommand = (pm: PackageManager) =>
|
|
127
|
+
pm === "deno" ? "deno task dev" : pm === "bun" ? "bun dev" : `${pm} run dev`;
|
|
136
128
|
|
|
137
129
|
async function isCommandAvailable(
|
|
138
130
|
{ checkCommand, outputPattern }: {
|
|
@@ -181,20 +173,23 @@ export const isDirectoryEmpty = async (
|
|
|
181
173
|
}
|
|
182
174
|
};
|
|
183
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
|
+
|
|
184
186
|
export const getNextInitCommand = (
|
|
185
187
|
pm: PackageManager,
|
|
186
|
-
): string[] => [
|
|
187
|
-
...createNextAppCommand(pm),
|
|
188
|
-
".",
|
|
189
|
-
"--ts",
|
|
190
|
-
"--app",
|
|
191
|
-
"--biome",
|
|
192
|
-
"--skip-install",
|
|
193
|
-
];
|
|
188
|
+
): string[] => [...createNextAppCommand(pm), ".", "--yes"];
|
|
194
189
|
|
|
195
190
|
const createNextAppCommand = (pm: PackageManager): string[] =>
|
|
196
191
|
pm === "deno"
|
|
197
|
-
? ["deno", "
|
|
192
|
+
? ["deno", "-Ar", "npm:create-next-app@latest"]
|
|
198
193
|
: pm === "bun"
|
|
199
194
|
? ["bun", "create", "next-app"]
|
|
200
195
|
: pm === "npm"
|
|
@@ -208,6 +203,10 @@ export const getNitroInitCommand = (
|
|
|
208
203
|
pm === "deno" ? "npm:giget@latest" : "giget@latest",
|
|
209
204
|
"nitro",
|
|
210
205
|
".",
|
|
206
|
+
"&&",
|
|
207
|
+
"rm",
|
|
208
|
+
"nitro.config.ts", // Remove default nitro config file
|
|
209
|
+
// This file will be created from template
|
|
211
210
|
];
|
|
212
211
|
|
|
213
212
|
const createNitroAppCommand = (pm: PackageManager): string[] =>
|
|
@@ -218,3 +217,7 @@ const createNitroAppCommand = (pm: PackageManager): string[] =>
|
|
|
218
217
|
: pm === "npm"
|
|
219
218
|
? ["npx"]
|
|
220
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
|
+
});
|