@fedify/init 2.4.0-dev.1797 → 2.4.0-dev.1827
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/dist/action/patch.js +3 -1
- package/dist/action/templates.js +14 -2
- package/dist/ask/kv.js +1 -1
- package/dist/ask/mq.js +1 -1
- package/dist/ask/pm.js +44 -26
- package/dist/const.js +4 -1
- package/dist/deno.js +1 -1
- package/dist/json/rt.js +7 -14
- package/dist/json/rt.json +7 -20
- package/dist/lib.js +78 -2
- package/dist/templates/defaults/smoke.test.ts.tpl +170 -0
- package/dist/test/db.js +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/utils.js +1 -1
- package/dist/webframeworks/astro.js +10 -4
- package/dist/webframeworks/bare-bones.js +7 -3
- package/dist/webframeworks/elysia.js +7 -3
- package/dist/webframeworks/express.js +7 -3
- package/dist/webframeworks/hono.js +7 -3
- package/dist/webframeworks/next.js +9 -4
- package/dist/webframeworks/nitro.js +11 -4
- package/dist/webframeworks/nuxt.js +9 -4
- package/dist/webframeworks/solidstart.js +9 -4
- package/dist/webframeworks/sveltekit.js +9 -4
- package/dist/webframeworks/utils.js +15 -1
- package/package.json +1 -1
package/dist/action/patch.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createFile, throwUnlessNotExists } from "../lib.js";
|
|
|
3
3
|
import { displayFile, noticeFilesToCreate, noticeFilesToInsert } from "./notice.js";
|
|
4
4
|
import { joinDir, stringifyEnvs } from "./utils.js";
|
|
5
5
|
import { devToolConfigs, loadDenoConfig, loadOxfmtConfig, loadOxlintConfig, loadPackageJson, loadTsConfig, loadVscodeExtensions, loadVscodeSettings } from "./configs.js";
|
|
6
|
-
import { getImports, loadFederation, loadLogging } from "./templates.js";
|
|
6
|
+
import { getImports, loadFederation, loadLogging, loadTest } from "./templates.js";
|
|
7
7
|
import { always, apply, entries, map, pipe, pipeLazy, tap } from "@fxts/core";
|
|
8
8
|
import { toMerged } from "es-toolkit";
|
|
9
9
|
import { access, readFile } from "node:fs/promises";
|
|
@@ -80,6 +80,7 @@ const getFiles = async (data) => ({
|
|
|
80
80
|
...data
|
|
81
81
|
}),
|
|
82
82
|
[data.initializer.loggingFile]: await loadLogging(data),
|
|
83
|
+
[data.initializer.testFile]: await loadTest(data),
|
|
83
84
|
".env": stringifyEnvs(data.env),
|
|
84
85
|
...data.initializer.files
|
|
85
86
|
});
|
|
@@ -119,6 +120,7 @@ const getJsons = (data) => {
|
|
|
119
120
|
const getGeneratedFilePaths = (data) => [
|
|
120
121
|
data.initializer.federationFile,
|
|
121
122
|
data.initializer.loggingFile,
|
|
123
|
+
data.initializer.testFile,
|
|
122
124
|
".env",
|
|
123
125
|
...Object.keys(data.initializer.files ?? {}),
|
|
124
126
|
...Object.keys(getJsons(data)),
|
package/dist/action/templates.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { replace } from "../utils.js";
|
|
2
|
-
import { readTemplate } from "../lib.js";
|
|
2
|
+
import { getDevCommand, readTemplate } from "../lib.js";
|
|
3
3
|
import { needsDenoDotenv } from "./utils.js";
|
|
4
4
|
import { concat, entries, join, map, pipe, when } from "@fxts/core";
|
|
5
5
|
import { toMerged } from "es-toolkit";
|
|
@@ -30,6 +30,18 @@ const getFederationTemplate = (packageManager) => packageManager === "deno" ? "d
|
|
|
30
30
|
*/
|
|
31
31
|
const loadLogging = async ({ projectName, initializer }) => pipe(await readTemplate(initializer.loggingTemplate ?? "defaults/logging.ts"), replace(/\/\* project name \*\//, JSON.stringify(projectName)));
|
|
32
32
|
/**
|
|
33
|
+
* Loads the smoke-test script content for the initializer.
|
|
34
|
+
*
|
|
35
|
+
* Every framework shares the same *defaults/smoke.test.ts* template, so unlike
|
|
36
|
+
* {@link loadLogging} there is no per-framework template override. The
|
|
37
|
+
* template spawns the project's own dev server, so it needs the dev command
|
|
38
|
+
* for the chosen package manager baked in at generation time.
|
|
39
|
+
*
|
|
40
|
+
* @param param0 - {@link InitCommandData} containing `packageManager`
|
|
41
|
+
* @returns The complete smoke-test script content as a string
|
|
42
|
+
*/
|
|
43
|
+
const loadTest = async ({ packageManager }) => pipe(await readTemplate("defaults/smoke.test.ts"), replace(/\/\* dev command \*\//, JSON.stringify(getDevCommand(packageManager).split(" ")).replaceAll(",", ", ")));
|
|
44
|
+
/**
|
|
33
45
|
* Generates import statements for KV store and message queue dependencies.
|
|
34
46
|
* Merges imports from both KV and MQ configurations and creates proper
|
|
35
47
|
* ES module import syntax.
|
|
@@ -75,4 +87,4 @@ const ENV_REG_EXP = /process\.env\.(\w+)/g;
|
|
|
75
87
|
*/
|
|
76
88
|
const convertEnv = (obj, pm) => pm === "deno" && ENV_REG_EXP.test(obj) ? obj.replaceAll(ENV_REG_EXP, (_, g1) => `Deno.env.get("${g1}")`) : obj;
|
|
77
89
|
//#endregion
|
|
78
|
-
export { convertEnv, getAlias, getImports, loadFederation, loadLogging };
|
|
90
|
+
export { convertEnv, getAlias, getImports, loadFederation, loadLogging, loadTest };
|
package/dist/ask/kv.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { KV_STORE } from "../const.js";
|
|
1
2
|
import { printErrorMessage } from "../utils.js";
|
|
2
3
|
import { isTest, kvStores } from "../lib.js";
|
|
3
|
-
import { KV_STORE } from "../const.js";
|
|
4
4
|
import { select } from "@inquirer/prompts";
|
|
5
5
|
import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js";
|
|
6
6
|
//#region src/ask/kv.ts
|
package/dist/ask/mq.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { MESSAGE_QUEUE } from "../const.js";
|
|
1
2
|
import { printErrorMessage } from "../utils.js";
|
|
2
3
|
import { isTest, messageQueues } from "../lib.js";
|
|
3
|
-
import { MESSAGE_QUEUE } from "../const.js";
|
|
4
4
|
import { select } from "@inquirer/prompts";
|
|
5
5
|
import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js";
|
|
6
6
|
//#region src/ask/mq.ts
|
package/dist/ask/pm.js
CHANGED
|
@@ -1,46 +1,64 @@
|
|
|
1
|
-
import { getInstallUrl, isPackageManagerAvailable, kvStores, messageQueues, packageManagers, runtimes } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { printErrorMessage } from "../utils.js";
|
|
3
|
+
import { checkAllRuntimes, getInstallUrl, isPackageManagerAvailable, isTest, runtimes } from "../lib.js";
|
|
4
|
+
import { pmToRt } from "../webframeworks/utils.js";
|
|
3
5
|
import webFrameworks from "../webframeworks/mod.js";
|
|
4
|
-
import
|
|
6
|
+
import process from "node:process";
|
|
5
7
|
import { select } from "@inquirer/prompts";
|
|
6
|
-
import { message } from "@optique/core/message";
|
|
8
|
+
import { message, optionName, text } from "@optique/core/message";
|
|
7
9
|
import { print } from "@optique/run";
|
|
8
10
|
//#region src/ask/pm.ts
|
|
9
11
|
/**
|
|
10
12
|
* Fills in the package manager by prompting the user if not provided.
|
|
11
|
-
* Ensures the selected package manager is compatible with the chosen web
|
|
12
|
-
*
|
|
13
|
+
* Ensures the selected package manager is compatible with the chosen web
|
|
14
|
+
* framework and installed on the system. When an explicitly requested package
|
|
15
|
+
* manager is unavailable, informs the user and prompts again.
|
|
13
16
|
*
|
|
14
17
|
* @param options - Initialization options possibly containing a packageManager and webFramework
|
|
15
18
|
* @returns A promise resolving to options with a guaranteed packageManager
|
|
16
19
|
*/
|
|
17
20
|
const fillPackageManager = async ({ packageManager, ...options }) => {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
21
|
+
const choices = await calculateChoices(options.webFramework);
|
|
22
|
+
if (packageManager != null) {
|
|
23
|
+
const choice = choices.find(({ value }) => value === packageManager);
|
|
24
|
+
if (choice.disabled == null) return {
|
|
25
|
+
...options,
|
|
26
|
+
packageManager
|
|
27
|
+
};
|
|
28
|
+
print(message`${optionName(choice.name)} ${text(choice.disabled)}`);
|
|
29
|
+
if (isTest(options)) process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
20
32
|
...options,
|
|
21
|
-
packageManager:
|
|
33
|
+
packageManager: await askPackageManager(choices)
|
|
22
34
|
};
|
|
23
|
-
noticeInstallUrl(pm);
|
|
24
|
-
return await fillPackageManager(options);
|
|
25
35
|
};
|
|
26
|
-
const
|
|
36
|
+
const calculateChoices = async (wf) => {
|
|
37
|
+
const runtimeChecks = await checkAllRuntimes(webFrameworks[wf].minRuntimeVersions);
|
|
38
|
+
const choices = await Promise.all(PACKAGE_MANAGER.map(choicePackageManager(wf, runtimeChecks)));
|
|
39
|
+
if (choices.every((choice) => choice.disabled)) {
|
|
40
|
+
printErrorMessage`No package manager with a supported runtime is available for ${webFrameworks[wf].label}.`;
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return choices;
|
|
44
|
+
};
|
|
45
|
+
const askPackageManager = (choices) => select({
|
|
27
46
|
message: "Choose the package manager to use",
|
|
28
|
-
choices
|
|
29
|
-
});
|
|
30
|
-
const choicePackageManager = (wf) => (value) => ({
|
|
31
|
-
name: isWfSupportsPm(wf, value) ? value : `${value} (not supported with ${webFrameworks[wf].label})`,
|
|
32
|
-
value,
|
|
33
|
-
disabled: !isWfSupportsPm(wf, value)
|
|
47
|
+
choices
|
|
34
48
|
});
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const label =
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
const choicePackageManager = (wf, runtimeChecks) => async (value) => {
|
|
50
|
+
const check = runtimeChecks[pmToRt(value)];
|
|
51
|
+
const label = runtimes[pmToRt(value)].label;
|
|
52
|
+
const disabled = !isWfSupportsPm(wf, value) ? `not supported with ${webFrameworks[wf].label}` : check.status === "unsupported" ? `requires ${label} ${check.required} or later (detected: ${check.detected})` : check.status === "missing" ? `requires ${label} which is not installed` : check.status === "malformed" ? `could not detect ${label} version` : pmToRt(value) === "node" && !await isPackageManagerAvailable(value) ? `is not installed; install it from ${getInstallUrl(value)}` : "";
|
|
53
|
+
return disabled === "" ? {
|
|
54
|
+
name: value,
|
|
55
|
+
value
|
|
56
|
+
} : {
|
|
57
|
+
name: value,
|
|
58
|
+
value,
|
|
59
|
+
disabled
|
|
60
|
+
};
|
|
42
61
|
};
|
|
43
|
-
const
|
|
44
|
-
const whenHasLabel = (desc) => when((name) => name in desc, (name) => desc[name].label);
|
|
62
|
+
const isWfSupportsPm = (wf, pm) => webFrameworks[wf].packageManagers.includes(pm);
|
|
45
63
|
//#endregion
|
|
46
64
|
export { fillPackageManager as default };
|
package/dist/const.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import kv_default from "./json/kv.js";
|
|
2
2
|
import mq_default from "./json/mq.js";
|
|
3
|
+
import rt_default from "./json/rt.js";
|
|
3
4
|
//#region src/const.ts
|
|
4
5
|
/** All supported package manager identifiers, in display order. */
|
|
5
6
|
const PACKAGE_MANAGER = [
|
|
@@ -26,6 +27,8 @@ const WEB_FRAMEWORK = [
|
|
|
26
27
|
const MESSAGE_QUEUE = Object.keys(mq_default);
|
|
27
28
|
/** All supported key-value store backend identifiers. */
|
|
28
29
|
const KV_STORE = Object.keys(kv_default);
|
|
30
|
+
/** All supported runtime identifiers. */
|
|
31
|
+
const RUNTIME = Object.keys(rt_default);
|
|
29
32
|
/**
|
|
30
33
|
* External database services that need to be running for integration tests.
|
|
31
34
|
* Used by the test suite to check service availability before running tests.
|
|
@@ -37,4 +40,4 @@ const DB_TO_CHECK = [
|
|
|
37
40
|
"amqp"
|
|
38
41
|
];
|
|
39
42
|
//#endregion
|
|
40
|
-
export { DB_TO_CHECK, KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK };
|
|
43
|
+
export { DB_TO_CHECK, KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, RUNTIME, WEB_FRAMEWORK };
|
package/dist/deno.js
CHANGED
package/dist/json/rt.js
CHANGED
|
@@ -3,27 +3,20 @@ var rt_default = {
|
|
|
3
3
|
deno: {
|
|
4
4
|
"label": "Deno",
|
|
5
5
|
"checkCommand": ["deno", "--version"],
|
|
6
|
-
"outputPattern": "^deno\\s
|
|
6
|
+
"outputPattern": "^deno\\s+(\\d+\\.\\d+\\.\\d+\\b)",
|
|
7
|
+
"minVersion": "2.0.0"
|
|
7
8
|
},
|
|
8
9
|
bun: {
|
|
9
10
|
"label": "Bun",
|
|
10
11
|
"checkCommand": ["bun", "--version"],
|
|
11
|
-
"outputPattern": "
|
|
12
|
+
"outputPattern": "^(\\d+\\.\\d+\\.\\d+\\b)",
|
|
13
|
+
"minVersion": "1.1.0"
|
|
12
14
|
},
|
|
13
|
-
|
|
15
|
+
node: {
|
|
14
16
|
"label": "Node.js",
|
|
15
17
|
"checkCommand": ["node", "--version"],
|
|
16
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d
|
|
17
|
-
|
|
18
|
-
yarn: {
|
|
19
|
-
"label": "Node.js",
|
|
20
|
-
"checkCommand": ["node", "--version"],
|
|
21
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d+$"
|
|
22
|
-
},
|
|
23
|
-
npm: {
|
|
24
|
-
"label": "Node.js",
|
|
25
|
-
"checkCommand": ["node", "--version"],
|
|
26
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d+$"
|
|
18
|
+
"outputPattern": "^v(\\d+\\.\\d+\\.\\d+\\b)",
|
|
19
|
+
"minVersion": "22.0.0"
|
|
27
20
|
}
|
|
28
21
|
};
|
|
29
22
|
//#endregion
|
package/dist/json/rt.json
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"deno",
|
|
6
6
|
"--version"
|
|
7
7
|
],
|
|
8
|
-
"outputPattern": "^deno\\s
|
|
8
|
+
"outputPattern": "^deno\\s+(\\d+\\.\\d+\\.\\d+\\b)",
|
|
9
|
+
"minVersion": "2.0.0"
|
|
9
10
|
},
|
|
10
11
|
"bun": {
|
|
11
12
|
"label": "Bun",
|
|
@@ -13,30 +14,16 @@
|
|
|
13
14
|
"bun",
|
|
14
15
|
"--version"
|
|
15
16
|
],
|
|
16
|
-
"outputPattern": "
|
|
17
|
+
"outputPattern": "^(\\d+\\.\\d+\\.\\d+\\b)",
|
|
18
|
+
"minVersion": "1.1.0"
|
|
17
19
|
},
|
|
18
|
-
"
|
|
20
|
+
"node": {
|
|
19
21
|
"label": "Node.js",
|
|
20
22
|
"checkCommand": [
|
|
21
23
|
"node",
|
|
22
24
|
"--version"
|
|
23
25
|
],
|
|
24
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d
|
|
25
|
-
|
|
26
|
-
"yarn": {
|
|
27
|
-
"label": "Node.js",
|
|
28
|
-
"checkCommand": [
|
|
29
|
-
"node",
|
|
30
|
-
"--version"
|
|
31
|
-
],
|
|
32
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d+$"
|
|
33
|
-
},
|
|
34
|
-
"npm": {
|
|
35
|
-
"label": "Node.js",
|
|
36
|
-
"checkCommand": [
|
|
37
|
-
"node",
|
|
38
|
-
"--version"
|
|
39
|
-
],
|
|
40
|
-
"outputPattern": "^v\\d+\\.\\d+\\.\\d+$"
|
|
26
|
+
"outputPattern": "^v(\\d+\\.\\d+\\.\\d+\\b)",
|
|
27
|
+
"minVersion": "22.0.0"
|
|
41
28
|
}
|
|
42
29
|
}
|
package/dist/lib.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { version } from "./deno.js";
|
|
2
2
|
import kv_default from "./json/kv.js";
|
|
3
3
|
import mq_default from "./json/mq.js";
|
|
4
|
-
import pm_default from "./json/pm.js";
|
|
5
4
|
import rt_default from "./json/rt.js";
|
|
5
|
+
import { RUNTIME } from "./const.js";
|
|
6
|
+
import pm_default from "./json/pm.js";
|
|
6
7
|
import { CommandError, isNotFoundError, runSubCommand } from "./utils.js";
|
|
7
8
|
import { entries, evolve, fromEntries, isObject, map, negate, pipe, throwIf } from "@fxts/core";
|
|
8
9
|
import process from "node:process";
|
|
@@ -119,6 +120,81 @@ async function isCommandAvailable({ checkCommand, outputPattern }) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
/**
|
|
123
|
+
* Compares two dotted version strings segment by segment and returns whether
|
|
124
|
+
* `detected` is higher than or equal to `required`.
|
|
125
|
+
*/
|
|
126
|
+
function verifyRuntimeVersion(detected, required) {
|
|
127
|
+
const detectedParts = detected.split(".").map(Number);
|
|
128
|
+
const requiredParts = required.split(".").map(Number);
|
|
129
|
+
for (let i = 0; i < Math.max(detectedParts.length, requiredParts.length); i++) {
|
|
130
|
+
const detectedPart = detectedParts[i] ?? 0;
|
|
131
|
+
const requiredPart = requiredParts[i] ?? 0;
|
|
132
|
+
if (detectedPart > requiredPart) return true;
|
|
133
|
+
if (detectedPart < requiredPart) return false;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Runs a runtime's version command and classifies the result as `"ok"`,
|
|
139
|
+
* `"unsupported"`, `"missing"`, or `"malformed"` against its `minVersion`.
|
|
140
|
+
*/
|
|
141
|
+
async function checkRuntimeVersion({ checkCommand, outputPattern, minVersion }) {
|
|
142
|
+
try {
|
|
143
|
+
const { stdout } = await $`${checkCommand}`.stdout("piped").spawn();
|
|
144
|
+
logger.debug("The stdout of the command {command} is: {stdout}", {
|
|
145
|
+
command: checkCommand,
|
|
146
|
+
stdout
|
|
147
|
+
});
|
|
148
|
+
const detected = outputPattern.exec(stdout.trim())?.[1] ?? null;
|
|
149
|
+
if (detected == null) return {
|
|
150
|
+
status: "malformed",
|
|
151
|
+
detected: null,
|
|
152
|
+
required: minVersion
|
|
153
|
+
};
|
|
154
|
+
if (!verifyRuntimeVersion(detected, minVersion)) return {
|
|
155
|
+
status: "unsupported",
|
|
156
|
+
detected,
|
|
157
|
+
required: minVersion
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
status: "ok",
|
|
161
|
+
detected,
|
|
162
|
+
required: minVersion
|
|
163
|
+
};
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (isNotFoundError(error)) return {
|
|
166
|
+
status: "missing",
|
|
167
|
+
detected: null,
|
|
168
|
+
required: minVersion
|
|
169
|
+
};
|
|
170
|
+
logger.debug("The command {command} failed with the error: {error}", {
|
|
171
|
+
command: checkCommand,
|
|
172
|
+
error
|
|
173
|
+
});
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Resolves the required version for `runtime` as the higher of its base minimum
|
|
179
|
+
* and an optional framework `override`.
|
|
180
|
+
*/
|
|
181
|
+
function resolveRequiredVersion(runtime, override) {
|
|
182
|
+
const base = runtimes[runtime].minVersion;
|
|
183
|
+
return override != null && verifyRuntimeVersion(override, base) ? override : base;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Checks every supported runtime once and returns a map from each runtime
|
|
187
|
+
* identifier to its version-check result, applying framework `overrides` on
|
|
188
|
+
* top of each runtime's base minimum.
|
|
189
|
+
*/
|
|
190
|
+
async function checkAllRuntimes(overrides = {}) {
|
|
191
|
+
const checked = await Promise.all(RUNTIME.map(async (runtime) => [runtime, await checkRuntimeVersion({
|
|
192
|
+
...runtimes[runtime],
|
|
193
|
+
minVersion: resolveRequiredVersion(runtime, overrides[runtime])
|
|
194
|
+
})]));
|
|
195
|
+
return Object.fromEntries(checked);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
122
198
|
* Creates a file at the given path with the given content, creating
|
|
123
199
|
* any necessary parent directories along the way.
|
|
124
200
|
*/
|
|
@@ -291,4 +367,4 @@ const isDirectory = async (path) => {
|
|
|
291
367
|
/** Returns `true` if the current run is in test mode. */
|
|
292
368
|
const isTest = ({ testMode }) => testMode;
|
|
293
369
|
//#endregion
|
|
294
|
-
export { PACKAGE_VERSION, createFile, getBuildCommand, getDevCommand, getInstallUrl, isDirectoryEmpty, isPackageManagerAvailable, isTest, kvStores, logger, messageQueues, packageManagers, readTemplate, runtimes, throwUnlessNotExists };
|
|
370
|
+
export { PACKAGE_VERSION, checkAllRuntimes, createFile, getBuildCommand, getDevCommand, getInstallUrl, isDirectoryEmpty, isPackageManagerAvailable, isTest, kvStores, logger, messageQueues, packageManagers, readTemplate, resolveRequiredVersion, runtimes, throwUnlessNotExists, verifyRuntimeVersion };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { getDocumentLoader } from "@fedify/fedify";
|
|
2
|
+
import { type Actor, isActor, lookupObject } from "@fedify/vocab";
|
|
3
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
|
+
import type { Readable } from "node:stream";
|
|
5
|
+
|
|
6
|
+
const DEV_COMMAND: string[] = /* dev command */;
|
|
7
|
+
const HANDLE = "john";
|
|
8
|
+
const STARTUP_TIMEOUT = 15_000;
|
|
9
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
10
|
+
|
|
11
|
+
async function main(): Promise<void> {
|
|
12
|
+
const [command, ...args] = DEV_COMMAND;
|
|
13
|
+
const server = spawn(command, args, {
|
|
14
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
15
|
+
shell: IS_WINDOWS,
|
|
16
|
+
windowsHide: true,
|
|
17
|
+
detached: !IS_WINDOWS,
|
|
18
|
+
});
|
|
19
|
+
server.on("error", () => {});
|
|
20
|
+
|
|
21
|
+
const exitOnSignal = () => {
|
|
22
|
+
stopServer(server);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
};
|
|
25
|
+
process.once("SIGINT", exitOnSignal);
|
|
26
|
+
process.once("SIGTERM", exitOnSignal);
|
|
27
|
+
|
|
28
|
+
let output = "";
|
|
29
|
+
const collectOutput = (stream: Readable | null) => {
|
|
30
|
+
const decoder = new TextDecoder();
|
|
31
|
+
stream?.on("data", (chunk: Buffer) => {
|
|
32
|
+
output += decoder.decode(chunk, { stream: true });
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
collectOutput(server.stdout);
|
|
36
|
+
collectOutput(server.stderr);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const port = await determinePort(server);
|
|
40
|
+
const target = `http://localhost:${port}/users/${HANDLE}`;
|
|
41
|
+
await waitForServer(target);
|
|
42
|
+
console.log(`Server is up at http://localhost:${port}.`);
|
|
43
|
+
const actor = await checkActor(target);
|
|
44
|
+
console.log(actor);
|
|
45
|
+
console.log(`Smoke test passed: ${target} resolved to an actor.`);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error("Smoke test failed:", error instanceof Error ? error.message : error);
|
|
48
|
+
if (output.trim() !== "") {
|
|
49
|
+
console.error(`\nDev server output:\n${output}`);
|
|
50
|
+
}
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
} finally {
|
|
53
|
+
stopServer(server);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function stripEscape(text: string): string {
|
|
58
|
+
return text.replace(new RegExp("\\u001B\\[[0-9;]*[A-Za-z]", "g"), "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function determinePort(server: ReturnType<typeof spawn>): Promise<number> {
|
|
62
|
+
const portPatterns = [
|
|
63
|
+
/listening on.*:(\d+)/i,
|
|
64
|
+
/server.*:(\d+)/i,
|
|
65
|
+
/https?:\/\/localhost:(\d+)/i,
|
|
66
|
+
/https?:\/\/0\.0\.0\.0:(\d+)/i,
|
|
67
|
+
/https?:\/\/127\.0\.0\.1:(\d+)/i,
|
|
68
|
+
/https?:\/\/[^:]+:(\d+)/i,
|
|
69
|
+
];
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const timeout = setTimeout(() => {
|
|
72
|
+
reject(
|
|
73
|
+
new Error(
|
|
74
|
+
`Timeout: Could not determine port from server output within ${STARTUP_TIMEOUT}ms.`,
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
}, STARTUP_TIMEOUT);
|
|
78
|
+
|
|
79
|
+
const findPort = (text: string) => {
|
|
80
|
+
for (const pattern of portPatterns) {
|
|
81
|
+
const match = text.match(pattern);
|
|
82
|
+
if (match && match[1]) {
|
|
83
|
+
const port = Number.parseInt(match[1], 10);
|
|
84
|
+
if (port > 0 && port < 65536) return port;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const scan = (stream: Readable | null) => {
|
|
91
|
+
const decoder = new TextDecoder();
|
|
92
|
+
let text = "";
|
|
93
|
+
stream?.on("data", (chunk: Buffer) => {
|
|
94
|
+
text += decoder.decode(chunk, { stream: true });
|
|
95
|
+
const port = findPort(stripEscape(text));
|
|
96
|
+
if (port != null) {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
resolve(port);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
scan(server.stdout);
|
|
104
|
+
scan(server.stderr);
|
|
105
|
+
server.once("exit", (code) => {
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
reject(new Error(`The dev server exited early with code ${String(code)}.`));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function waitForServer(url: string): Promise<void> {
|
|
113
|
+
const startTime = Date.now();
|
|
114
|
+
let lastStatus: number | undefined;
|
|
115
|
+
|
|
116
|
+
while (Date.now() - startTime < STARTUP_TIMEOUT) {
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(url, {
|
|
119
|
+
headers: { Accept: "application/activity+json" },
|
|
120
|
+
signal: AbortSignal.timeout(1000),
|
|
121
|
+
});
|
|
122
|
+
await response.body?.cancel();
|
|
123
|
+
if (response.ok) return;
|
|
124
|
+
lastStatus = response.status;
|
|
125
|
+
} catch {
|
|
126
|
+
// Server not ready yet, continue waiting
|
|
127
|
+
}
|
|
128
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
129
|
+
}
|
|
130
|
+
throw new Error(
|
|
131
|
+
`The server did not become ready within ${STARTUP_TIMEOUT}ms.` +
|
|
132
|
+
(lastStatus == null ? "" : ` Last response status: ${lastStatus}.`),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function checkActor(url: string): Promise<Actor> {
|
|
137
|
+
const object = await lookupObject(url, {
|
|
138
|
+
documentLoader: getDocumentLoader({ allowPrivateAddress: true }),
|
|
139
|
+
});
|
|
140
|
+
if (object == null) {
|
|
141
|
+
throw new Error(`Could not resolve an actor at ${url}.`);
|
|
142
|
+
}
|
|
143
|
+
if (!isActor(object)) {
|
|
144
|
+
throw new Error(`Expected an actor at ${url}, but got a non-actor object.`);
|
|
145
|
+
}
|
|
146
|
+
return object;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function stopServer(server: ReturnType<typeof spawn>): void {
|
|
150
|
+
if (server.pid == null) return;
|
|
151
|
+
if (IS_WINDOWS) {
|
|
152
|
+
spawnSync("taskkill", ["/pid", String(server.pid), "/T", "/F"], {
|
|
153
|
+
stdio: "ignore",
|
|
154
|
+
windowsHide: true,
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
process.kill(-server.pid, "SIGKILL");
|
|
160
|
+
} catch {
|
|
161
|
+
// Process group already exited.
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
server.kill("SIGKILL");
|
|
165
|
+
} catch {
|
|
166
|
+
// Process already exited.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
await main();
|
package/dist/test/db.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { printErrorMessage, printMessage } from "../utils.js";
|
|
2
1
|
import { DB_TO_CHECK } from "../const.js";
|
|
2
|
+
import { printErrorMessage, printMessage } from "../utils.js";
|
|
3
3
|
import db_to_check_default from "../json/db-to-check.js";
|
|
4
4
|
import { concat, filter, pipe, toArray, uniq } from "@fxts/core";
|
|
5
5
|
import { createConnection } from "node:net";
|
package/dist/types.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ interface WebFrameworkInitializer {
|
|
|
30
30
|
federationFile: string;
|
|
31
31
|
/** Relative path where the logging configuration file will be created. */
|
|
32
32
|
loggingFile: string;
|
|
33
|
+
/** Relative path where the smoke-test script file will be created. */
|
|
34
|
+
testFile: string;
|
|
33
35
|
/** Optional template path for the logging configuration file. */
|
|
34
36
|
loggingTemplate?: string;
|
|
35
37
|
/**
|
package/dist/utils.js
CHANGED
|
@@ -65,7 +65,7 @@ const createJsonDepthGuard = () => {
|
|
|
65
65
|
/** Checks whether a string or array-like value has a length greater than zero. */
|
|
66
66
|
const notEmpty = (s) => s.length > 0;
|
|
67
67
|
/** Type guard that checks whether an error is a "file not found" (`ENOENT`) error. */
|
|
68
|
-
const isNotFoundError = (e) => isObject(e) && "code" in e && e.code === "ENOENT";
|
|
68
|
+
const isNotFoundError = (e) => isObject(e) && ("code" in e && e.code === "ENOENT" || "exitCode" in e && e.exitCode === 127);
|
|
69
69
|
/**
|
|
70
70
|
* Error thrown when a spawned shell command exits with a non-zero code.
|
|
71
71
|
* Captures stdout, stderr, exit code, and the original command array.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "@logtape/logtape" as _logtape_logtape, "npm:@astrojs/node" as npm__astrojs_node, "npm:@deno/astro-adapter" as npm__deno_astro_adapter, "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@types/node@22" as npm__types_node_22, "npm:astro" as npm_astro, "npm:create-astro" as npm_create_astro, "npm:oxlint" as npm_oxlint, "npm:prettier" as npm_prettier, "npm:prettier-plugin-astro" as npm_prettier_plugin_astro, "npm:typescript" as npm_typescript } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestDependencies, getTestTask, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/astro.ts
|
|
7
7
|
const astroNodeBunDevDependencies = {
|
|
8
8
|
"@fedify/lint": PACKAGE_VERSION,
|
|
@@ -27,6 +27,7 @@ const astroDescription = {
|
|
|
27
27
|
label: "Astro",
|
|
28
28
|
packageManagers: PACKAGE_MANAGER,
|
|
29
29
|
defaultPort: 4321,
|
|
30
|
+
minRuntimeVersions: { node: "22.12.0" },
|
|
30
31
|
init: async ({ packageManager: pm }) => {
|
|
31
32
|
const dependencies = pm === "deno" ? {
|
|
32
33
|
...defaultDenoDependencies,
|
|
@@ -54,10 +55,12 @@ const astroDescription = {
|
|
|
54
55
|
...pm !== "deno" ? {
|
|
55
56
|
typescript: npm_typescript,
|
|
56
57
|
"@types/node": npm__types_node_22
|
|
57
|
-
} : {}
|
|
58
|
+
} : {},
|
|
59
|
+
...getTestDependencies(pm)
|
|
58
60
|
},
|
|
59
61
|
federationFile: "src/federation.ts",
|
|
60
62
|
loggingFile: "src/logging.ts",
|
|
63
|
+
testFile: "scripts/smoke.test.ts",
|
|
61
64
|
format: pm === "deno" ? void 0 : { tool: "prettier" },
|
|
62
65
|
files: {
|
|
63
66
|
"astro.config.ts": await readTemplate(`astro/astro.config.${pmToRt(pm)}.ts`),
|
|
@@ -106,18 +109,21 @@ const TASKS = {
|
|
|
106
109
|
"deno": {
|
|
107
110
|
dev: `${astroDenoCommand} dev`,
|
|
108
111
|
build: `${astroDenoCommand} build`,
|
|
109
|
-
preview: `${astroDenoCommand} preview
|
|
112
|
+
preview: `${astroDenoCommand} preview`,
|
|
113
|
+
test: getTestTask("deno")
|
|
110
114
|
},
|
|
111
115
|
"bun": {
|
|
112
116
|
dev: "bunx --bun astro dev",
|
|
113
117
|
build: "bunx --bun astro build",
|
|
114
118
|
preview: "bun ./dist/server/entry.mjs",
|
|
119
|
+
test: getTestTask("bun"),
|
|
115
120
|
...astroNodeBunDevToolTasks
|
|
116
121
|
},
|
|
117
122
|
"node": {
|
|
118
123
|
dev: "dotenvx run -- astro dev",
|
|
119
124
|
build: "dotenvx run -- astro build",
|
|
120
125
|
preview: "dotenvx run -- astro preview",
|
|
126
|
+
test: getTestTask("npm"),
|
|
121
127
|
...astroNodeBunDevToolTasks
|
|
122
128
|
}
|
|
123
129
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { readTemplate } from "../lib.js";
|
|
3
3
|
import { "@hongminhee/x-forwarded-fetch" as _hongminhee_x_forwarded_fetch, "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@hono/node-server" as npm__hono_node_server, "npm:@types/bun" as npm__types_bun, "npm:@types/node@25" as npm__types_node_25, "npm:tsx" as npm_tsx, "npm:x-forwarded-fetch" as npm_x_forwarded_fetch } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/bare-bones.ts
|
|
7
7
|
const bareBonesDescription = {
|
|
8
8
|
label: "Bare-bones",
|
|
@@ -16,6 +16,7 @@ const bareBonesDescription = {
|
|
|
16
16
|
},
|
|
17
17
|
federationFile: "src/federation.ts",
|
|
18
18
|
loggingFile: "src/logging.ts",
|
|
19
|
+
testFile: "scripts/smoke.test.ts",
|
|
19
20
|
files: { "src/main.ts": await readTemplate(`bare-bones/main/${pmToRt(pm)}.ts`) },
|
|
20
21
|
compilerOptions: pm === "deno" ? {
|
|
21
22
|
"jsx": "precompile",
|
|
@@ -46,16 +47,19 @@ const getDependencies = (pm) => pm === "deno" ? {
|
|
|
46
47
|
const TASKS = {
|
|
47
48
|
deno: {
|
|
48
49
|
dev: "deno run -A --watch ./src/main.ts",
|
|
49
|
-
prod: "deno run -A ./src/main.ts"
|
|
50
|
+
prod: "deno run -A ./src/main.ts",
|
|
51
|
+
test: getTestTask("deno")
|
|
50
52
|
},
|
|
51
53
|
bun: {
|
|
52
54
|
dev: "bun run --hot ./src/main.ts",
|
|
53
55
|
prod: "bun run ./src/main.ts",
|
|
56
|
+
test: getTestTask("bun"),
|
|
54
57
|
...nodeBunDevToolTasks
|
|
55
58
|
},
|
|
56
59
|
node: {
|
|
57
60
|
dev: "dotenvx run -- tsx watch ./src/main.ts",
|
|
58
61
|
prod: "dotenvx run -- node --import tsx ./src/main.ts",
|
|
62
|
+
test: getTestTask("npm"),
|
|
59
63
|
...nodeBunDevToolTasks
|
|
60
64
|
}
|
|
61
65
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@elysiajs/node" as npm__elysiajs_node, "npm:@sinclair/typebox" as npm__sinclair_typebox, "npm:@types/bun" as npm__types_bun, "npm:@types/node@25" as npm__types_node_25, "npm:elysia" as npm_elysia, "npm:openapi-types" as npm_openapi_types, "npm:tsx" as npm_tsx, "npm:typescript" as npm_typescript } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/elysia.ts
|
|
7
7
|
const elysiaDescription = {
|
|
8
8
|
label: "Elysia",
|
|
@@ -36,6 +36,7 @@ const elysiaDescription = {
|
|
|
36
36
|
},
|
|
37
37
|
federationFile: "src/federation.ts",
|
|
38
38
|
loggingFile: "src/logging.ts",
|
|
39
|
+
testFile: "scripts/smoke.test.ts",
|
|
39
40
|
files: { "src/index.ts": (await readTemplate(`elysia/index/${pmToRt(pm)}.ts`)).replace(/\/\* logger \*\//, projectName) },
|
|
40
41
|
compilerOptions: pm === "deno" || pm === "bun" ? void 0 : {
|
|
41
42
|
"lib": ["ESNext", "DOM"],
|
|
@@ -54,17 +55,20 @@ const elysiaDescription = {
|
|
|
54
55
|
const TASKS = {
|
|
55
56
|
deno: {
|
|
56
57
|
dev: "deno serve --allow-read --allow-env --allow-net --watch ./src/index.ts",
|
|
57
|
-
prod: "deno serve --allow-read --allow-env --allow-net ./src/index.ts"
|
|
58
|
+
prod: "deno serve --allow-read --allow-env --allow-net ./src/index.ts",
|
|
59
|
+
test: getTestTask("deno")
|
|
58
60
|
},
|
|
59
61
|
bun: {
|
|
60
62
|
dev: "bun run --hot ./src/index.ts",
|
|
61
63
|
prod: "bun run ./src/index.ts",
|
|
64
|
+
test: getTestTask("bun"),
|
|
62
65
|
...nodeBunDevToolTasks
|
|
63
66
|
},
|
|
64
67
|
node: {
|
|
65
68
|
dev: "dotenvx run -- tsx watch src/index.ts",
|
|
66
69
|
build: "tsc src/index.ts --outDir dist",
|
|
67
70
|
start: "NODE_ENV=production dotenvx run -- node dist/index.js",
|
|
71
|
+
test: getTestTask("npm"),
|
|
68
72
|
...nodeBunDevToolTasks
|
|
69
73
|
}
|
|
70
74
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@types/bun" as npm__types_bun, "npm:@types/express" as npm__types_express, "npm:express" as npm_express, "npm:tsx" as npm_tsx } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/express.ts
|
|
7
7
|
const expressDescription = {
|
|
8
8
|
label: "Express",
|
|
@@ -25,6 +25,7 @@ const expressDescription = {
|
|
|
25
25
|
},
|
|
26
26
|
federationFile: "src/federation.ts",
|
|
27
27
|
loggingFile: "src/logging.ts",
|
|
28
|
+
testFile: "scripts/smoke.test.ts",
|
|
28
29
|
files: {
|
|
29
30
|
"src/app.ts": (await readTemplate("express/app.ts")).replace(/\/\* logger \*\//, projectName),
|
|
30
31
|
"src/index.ts": await readTemplate("express/index.ts")
|
|
@@ -46,16 +47,19 @@ const expressDescription = {
|
|
|
46
47
|
const TASKS = {
|
|
47
48
|
deno: {
|
|
48
49
|
dev: "deno run --allow-read --allow-net --allow-env --allow-sys --watch ./src/index.ts",
|
|
49
|
-
prod: "deno run --allow-read --allow-net --allow-env --allow-sys ./src/index.ts"
|
|
50
|
+
prod: "deno run --allow-read --allow-net --allow-env --allow-sys ./src/index.ts",
|
|
51
|
+
test: getTestTask("deno")
|
|
50
52
|
},
|
|
51
53
|
bun: {
|
|
52
54
|
dev: "bun run --hot ./src/index.ts",
|
|
53
55
|
prod: "bun run ./src/index.ts",
|
|
56
|
+
test: getTestTask("bun"),
|
|
54
57
|
...nodeBunDevToolTasks
|
|
55
58
|
},
|
|
56
59
|
node: {
|
|
57
60
|
dev: "dotenvx run -- tsx watch ./src/index.ts",
|
|
58
61
|
prod: "dotenvx run -- node --import tsx ./src/index.ts",
|
|
62
|
+
test: getTestTask("npm"),
|
|
59
63
|
...nodeBunDevToolTasks
|
|
60
64
|
}
|
|
61
65
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { PACKAGE_MANAGER } from "../const.js";
|
|
1
2
|
import { replace } from "../utils.js";
|
|
2
3
|
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
|
-
import { PACKAGE_MANAGER } from "../const.js";
|
|
4
4
|
import { "@hongminhee/x-forwarded-fetch" as _hongminhee_x_forwarded_fetch, "@hono/hono" as _hono_hono, "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@hono/node-server" as npm__hono_node_server, "npm:@types/bun" as npm__types_bun, "npm:hono" as npm_hono, "npm:tsx" as npm_tsx, "npm:x-forwarded-fetch" as npm_x_forwarded_fetch } from "../json/deps.js";
|
|
5
5
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
6
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
|
+
import { getInstruction, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
7
7
|
import { pipe } from "@fxts/core";
|
|
8
8
|
//#region src/webframeworks/hono.ts
|
|
9
9
|
const honoDescription = {
|
|
@@ -18,6 +18,7 @@ const honoDescription = {
|
|
|
18
18
|
},
|
|
19
19
|
federationFile: "src/federation.ts",
|
|
20
20
|
loggingFile: "src/logging.ts",
|
|
21
|
+
testFile: "scripts/smoke.test.ts",
|
|
21
22
|
files: {
|
|
22
23
|
"src/app.tsx": pipe(await readTemplate("hono/app.tsx"), replace(/\/\* hono \*\//, pm === "deno" ? "@hono/hono" : "hono"), replace(/\/\* logger \*\//, projectName)),
|
|
23
24
|
"src/index.ts": await readTemplate(`hono/index/${pmToRt(pm)}.ts`)
|
|
@@ -58,16 +59,19 @@ const getDependencies = (pm) => pm === "deno" ? {
|
|
|
58
59
|
const TASKS = {
|
|
59
60
|
deno: {
|
|
60
61
|
dev: "deno run -A --watch ./src/index.ts",
|
|
61
|
-
prod: "deno run -A ./src/index.ts"
|
|
62
|
+
prod: "deno run -A ./src/index.ts",
|
|
63
|
+
test: getTestTask("deno")
|
|
62
64
|
},
|
|
63
65
|
bun: {
|
|
64
66
|
dev: "bun run --hot ./src/index.ts",
|
|
65
67
|
prod: "bun run ./src/index.ts",
|
|
68
|
+
test: getTestTask("bun"),
|
|
66
69
|
...nodeBunDevToolTasks
|
|
67
70
|
},
|
|
68
71
|
node: {
|
|
69
72
|
dev: "dotenvx run -- tsx watch ./src/index.ts",
|
|
70
73
|
prod: "dotenvx run -- node --import tsx ./src/index.ts",
|
|
74
|
+
test: getTestTask("npm"),
|
|
71
75
|
...nodeBunDevToolTasks
|
|
72
76
|
}
|
|
73
77
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@types/node@20" as npm__types_node_20 } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, getNodeBunDevToolTasks } from "./utils.js";
|
|
5
|
+
import { getInstruction, getNodeBunDevToolTasks, getTestDependencies, getTestTask } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/next.ts
|
|
7
7
|
const nextDescription = {
|
|
8
8
|
label: "Next.js",
|
|
@@ -21,16 +21,21 @@ const nextDescription = {
|
|
|
21
21
|
},
|
|
22
22
|
devDependencies: {
|
|
23
23
|
"@types/node": npm__types_node_20,
|
|
24
|
-
...defaultDevDependencies
|
|
24
|
+
...defaultDevDependencies,
|
|
25
|
+
...getTestDependencies(pm)
|
|
25
26
|
},
|
|
26
27
|
federationFile: "federation/index.ts",
|
|
27
28
|
loggingFile: "logging.ts",
|
|
29
|
+
testFile: "scripts/smoke.test.ts",
|
|
28
30
|
format: { ignorePatterns: [".next/**"] },
|
|
29
31
|
files: {
|
|
30
32
|
"instrumentation.ts": await readTemplate("next/instrumentation.ts"),
|
|
31
33
|
"middleware.ts": await readTemplate("next/middleware.ts")
|
|
32
34
|
},
|
|
33
|
-
tasks:
|
|
35
|
+
tasks: {
|
|
36
|
+
...getNodeBunDevToolTasks(pm),
|
|
37
|
+
test: getTestTask(pm)
|
|
38
|
+
},
|
|
34
39
|
instruction: getInstruction(pm, 3e3)
|
|
35
40
|
})
|
|
36
41
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
4
|
-
import { getInstruction, getNodeBunDevToolTasks } from "./utils.js";
|
|
4
|
+
import { getInstruction, getNodeBunDevToolTasks, getTestDependencies, getTestTask } from "./utils.js";
|
|
5
5
|
//#region src/webframeworks/nitro.ts
|
|
6
6
|
const nitroDescription = {
|
|
7
7
|
label: "Nitro",
|
|
@@ -19,9 +19,13 @@ const nitroDescription = {
|
|
|
19
19
|
"@fedify/h3": PACKAGE_VERSION,
|
|
20
20
|
...pm === "deno" && defaultDenoDependencies
|
|
21
21
|
},
|
|
22
|
-
devDependencies:
|
|
22
|
+
devDependencies: {
|
|
23
|
+
...defaultDevDependencies,
|
|
24
|
+
...getTestDependencies(pm)
|
|
25
|
+
},
|
|
23
26
|
federationFile: "server/federation.ts",
|
|
24
27
|
loggingFile: "server/logging.ts",
|
|
28
|
+
testFile: "scripts/smoke.test.ts",
|
|
25
29
|
format: { ignorePatterns: [".output/**"] },
|
|
26
30
|
env: testMode ? { HOST: "127.0.0.1" } : {},
|
|
27
31
|
files: {
|
|
@@ -51,7 +55,10 @@ const nitroDescription = {
|
|
|
51
55
|
lib: ["ESNext", "DOM"],
|
|
52
56
|
baseUrl: "."
|
|
53
57
|
},
|
|
54
|
-
tasks:
|
|
58
|
+
tasks: {
|
|
59
|
+
...getNodeBunDevToolTasks(pm),
|
|
60
|
+
test: getTestTask(pm)
|
|
61
|
+
},
|
|
55
62
|
instruction: getInstruction(pm, 3e3)
|
|
56
63
|
})
|
|
57
64
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@types/node@25" as npm__types_node_25, "npm:typescript" as npm_typescript } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, getNodeBunDevToolTasks } from "./utils.js";
|
|
5
|
+
import { getInstruction, getNodeBunDevToolTasks, getTestDependencies, getTestTask } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/nuxt.ts
|
|
7
7
|
const nuxtDescription = {
|
|
8
8
|
label: "Nuxt",
|
|
@@ -14,18 +14,23 @@ const nuxtDescription = {
|
|
|
14
14
|
devDependencies: {
|
|
15
15
|
...defaultDevDependencies,
|
|
16
16
|
"typescript": npm_typescript,
|
|
17
|
-
"@types/node": npm__types_node_25
|
|
17
|
+
"@types/node": npm__types_node_25,
|
|
18
|
+
...getTestDependencies(pm)
|
|
18
19
|
},
|
|
19
20
|
federationFile: "server/federation.ts",
|
|
20
21
|
loggingFile: "server/logging.ts",
|
|
21
22
|
loggingTemplate: "nuxt/server/logging.ts",
|
|
23
|
+
testFile: "scripts/smoke.test.ts",
|
|
22
24
|
format: { ignorePatterns: [".output/**"] },
|
|
23
25
|
env: testMode ? { HOST: "127.0.0.1" } : {},
|
|
24
26
|
files: {
|
|
25
27
|
"nuxt.config.ts": await readTemplate("nuxt/nuxt.config.ts"),
|
|
26
28
|
"server/plugins/logging.ts": await readTemplate("nuxt/server/plugins/logging.ts")
|
|
27
29
|
},
|
|
28
|
-
tasks:
|
|
30
|
+
tasks: {
|
|
31
|
+
...getNodeBunDevToolTasks(pm),
|
|
32
|
+
test: getTestTask(pm)
|
|
33
|
+
},
|
|
29
34
|
instruction: getInstruction(pm, 3e3)
|
|
30
35
|
})
|
|
31
36
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@solidjs/router" as npm__solidjs_router, "npm:@solidjs/start" as npm__solidjs_start, "npm:@types/node@22" as npm__types_node_22, "npm:solid-js" as npm_solid_js, "npm:typescript" as npm_typescript, "npm:vinxi" as npm_vinxi } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestDependencies, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/solidstart.ts
|
|
7
7
|
const NPM_SOLIDSTART = `npm:@solidjs/start@${npm__solidjs_start}`;
|
|
8
8
|
const solidstartDescription = {
|
|
@@ -14,10 +14,12 @@ const solidstartDescription = {
|
|
|
14
14
|
devDependencies: {
|
|
15
15
|
...defaultDevDependencies,
|
|
16
16
|
typescript: npm_typescript,
|
|
17
|
-
"@types/node": npm__types_node_22
|
|
17
|
+
"@types/node": npm__types_node_22,
|
|
18
|
+
...getTestDependencies(pm)
|
|
18
19
|
},
|
|
19
20
|
federationFile: "src/federation.ts",
|
|
20
21
|
loggingFile: "src/logging.ts",
|
|
22
|
+
testFile: "scripts/smoke.test.ts",
|
|
21
23
|
format: { ignorePatterns: [".solid/**", ".vinxi/**"] },
|
|
22
24
|
files: {
|
|
23
25
|
"app.config.ts": (await readTemplate("solidstart/app.config.ts")).replace(/\/\* preset \*\//, pm === "deno" ? "deno-server" : "node-server"),
|
|
@@ -70,18 +72,21 @@ const TASKS = {
|
|
|
70
72
|
deno: {
|
|
71
73
|
dev: "deno run -A npm:vinxi dev",
|
|
72
74
|
build: "deno run -A npm:vinxi build",
|
|
73
|
-
start: "deno run -A npm:vinxi start"
|
|
75
|
+
start: "deno run -A npm:vinxi start",
|
|
76
|
+
test: getTestTask("deno")
|
|
74
77
|
},
|
|
75
78
|
bun: {
|
|
76
79
|
dev: "bunx vinxi dev",
|
|
77
80
|
build: "bunx vinxi build",
|
|
78
81
|
start: "bunx vinxi start",
|
|
82
|
+
test: getTestTask("bun"),
|
|
79
83
|
...nodeBunDevToolTasks
|
|
80
84
|
},
|
|
81
85
|
node: {
|
|
82
86
|
dev: "vinxi dev",
|
|
83
87
|
build: "vinxi build",
|
|
84
88
|
start: "dotenvx run -- vinxi start",
|
|
89
|
+
test: getTestTask("npm"),
|
|
85
90
|
...nodeBunDevToolTasks
|
|
86
91
|
}
|
|
87
92
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
2
1
|
import { PACKAGE_MANAGER } from "../const.js";
|
|
2
|
+
import { PACKAGE_VERSION, readTemplate } from "../lib.js";
|
|
3
3
|
import { "npm:@dotenvx/dotenvx" as npm__dotenvx_dotenvx, "npm:@types/node@25" as npm__types_node_25, "npm:typescript" as npm_typescript } from "../json/deps.js";
|
|
4
4
|
import { defaultDenoDependencies, defaultDevDependencies } from "./const.js";
|
|
5
|
-
import { getInstruction, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
5
|
+
import { getInstruction, getTestDependencies, getTestTask, nodeBunDevToolTasks, pmToRt } from "./utils.js";
|
|
6
6
|
//#region src/webframeworks/sveltekit.ts
|
|
7
7
|
const sveltekitDescription = {
|
|
8
8
|
label: "SvelteKit",
|
|
@@ -18,13 +18,18 @@ const sveltekitDescription = {
|
|
|
18
18
|
...defaultDevDependencies,
|
|
19
19
|
"typescript": npm_typescript,
|
|
20
20
|
"@types/node": npm__types_node_25,
|
|
21
|
-
...pmToRt(pm) === "deno" ? {} : { "@dotenvx/dotenvx": npm__dotenvx_dotenvx }
|
|
21
|
+
...pmToRt(pm) === "deno" ? {} : { "@dotenvx/dotenvx": npm__dotenvx_dotenvx },
|
|
22
|
+
...getTestDependencies(pm)
|
|
22
23
|
},
|
|
23
24
|
federationFile: "src/lib/federation.ts",
|
|
24
25
|
loggingFile: "src/lib/logging.ts",
|
|
26
|
+
testFile: "scripts/smoke.test.ts",
|
|
25
27
|
env: testMode ? { HOST: "127.0.0.1" } : {},
|
|
26
28
|
files: { "src/hooks.server.ts": await readTemplate("sveltekit/hooks.server.ts") },
|
|
27
|
-
tasks: pmToRt(pm) === "deno" ? {} : {
|
|
29
|
+
tasks: pmToRt(pm) === "deno" ? { test: getTestTask("deno") } : {
|
|
30
|
+
...TASKS,
|
|
31
|
+
test: getTestTask(pm)
|
|
32
|
+
},
|
|
28
33
|
instruction: getInstruction(pm, 5173)
|
|
29
34
|
})
|
|
30
35
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDevCommand } from "../lib.js";
|
|
2
|
+
import { "npm:tsx" as npm_tsx } from "../json/deps.js";
|
|
2
3
|
import { commandLine, message } from "@optique/core/message";
|
|
3
4
|
//#region src/webframeworks/utils.ts
|
|
4
5
|
const nodeBunDevToolTasks = {
|
|
@@ -7,6 +8,19 @@ const nodeBunDevToolTasks = {
|
|
|
7
8
|
lint: "oxlint ."
|
|
8
9
|
};
|
|
9
10
|
const getNodeBunDevToolTasks = (pm) => pm === "deno" ? {} : nodeBunDevToolTasks;
|
|
11
|
+
const SMOKE_TEST_FILE = "scripts/smoke.test.ts";
|
|
12
|
+
/**
|
|
13
|
+
* Returns the `test` task command that runs the generated smoke-test
|
|
14
|
+
* script (`WebFrameworkInitializer.testFile`) with the runtime matching the
|
|
15
|
+
* given package manager.
|
|
16
|
+
*/
|
|
17
|
+
const getTestTask = (pm) => pmToRt(pm) === "deno" ? `deno run -A ${SMOKE_TEST_FILE}` : pmToRt(pm) === "bun" ? `bun run ${SMOKE_TEST_FILE}` : `tsx ${SMOKE_TEST_FILE}`;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the dev dependencies the `test` task needs beyond what the
|
|
20
|
+
* framework already declares. Node.js runs the smoke-test script through
|
|
21
|
+
* `tsx`; Deno and Bun execute TypeScript natively.
|
|
22
|
+
*/
|
|
23
|
+
const getTestDependencies = (pm) => pmToRt(pm) === "node" ? { tsx: npm_tsx } : {};
|
|
10
24
|
/**
|
|
11
25
|
* Generates the post-initialization instruction message that shows
|
|
12
26
|
* the user how to start the dev server and look up an actor.
|
|
@@ -32,4 +46,4 @@ Then, try to look up an actor from your server:
|
|
|
32
46
|
*/
|
|
33
47
|
const pmToRt = (pm) => pm !== "deno" && pm !== "bun" ? "node" : pm;
|
|
34
48
|
//#endregion
|
|
35
|
-
export { getInstruction, getNodeBunDevToolTasks, nodeBunDevToolTasks, pmToRt };
|
|
49
|
+
export { getInstruction, getNodeBunDevToolTasks, getTestDependencies, getTestTask, nodeBunDevToolTasks, pmToRt };
|