create-riebeckite 0.0.14 → 0.0.16

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/README.md CHANGED
@@ -6,6 +6,15 @@ Create a new Riebeckite site from an official starter preset.
6
6
 
7
7
  ## Usage
8
8
 
9
+ Interactive (recommended): run without arguments and answer the prompts for
10
+ the project name, preset, content source, and deployment.
11
+
12
+ ```sh
13
+ npx create-riebeckite
14
+ ```
15
+
16
+ Passing any argument keeps the command non-interactive:
17
+
9
18
  ```sh
10
19
  npx create-riebeckite my-site
11
20
  cd my-site
@@ -13,6 +22,23 @@ npm install
13
22
  npm exec riebeckite build
14
23
  ```
15
24
 
25
+ ## Choices
26
+
27
+ The prompts ask, in order:
28
+
29
+ | Prompt | Options | Recommendation |
30
+ | --- | --- | --- |
31
+ | Project name | the folder to create (empty input uses the default name) | any name, e.g. `my-site` |
32
+ | Preset | `starter`, `minimal`, `showcase`, `empty` | `starter` for most sites |
33
+ | Content source | `This project`, `Separate GitHub repository` | `This project` to start |
34
+ | Deployment | `GitHub Actions + Cloudflare Workers`, `Not now` | `Not now` for local development |
35
+
36
+ `Separate GitHub repository` also asks for the content and site repositories,
37
+ and GitHub Actions deployment is then configured for you. That setup is covered
38
+ in the [content repository guide](../../docs/en/docs/guides/content-repositories.md).
39
+ Deployment can be added later, described in
40
+ [Deployment](../../docs/en/docs/getting-started/deployment.md).
41
+
16
42
  ## Options
17
43
 
18
44
  | Option | Description |
@@ -31,6 +57,8 @@ For example, scaffold the complete feature tour:
31
57
  npx create-riebeckite my-site --preset showcase
32
58
  ```
33
59
 
60
+ ## External content repository
61
+
34
62
  To use a separate content repository and deploy after its `main` branch is
35
63
  pushed, generate the common workflow once (the preset does not affect it):
36
64
 
package/README_ja.md CHANGED
@@ -7,6 +7,15 @@ CLI ツールです。
7
7
 
8
8
  ## 使い方
9
9
 
10
+ 対話モード(推奨): 引数なしで実行すると、プロジェクト名・preset・
11
+ コンテンツの格納場所・デプロイについて順に確認します。
12
+
13
+ ```sh
14
+ npx create-riebeckite
15
+ ```
16
+
17
+ 引数を 1 つでも渡すと、従来どおり非対話で動作します。
18
+
10
19
  ```sh
11
20
  npx create-riebeckite my-site
12
21
  cd my-site
@@ -14,6 +23,19 @@ npm install
14
23
  npm exec riebeckite build
15
24
  ```
16
25
 
26
+ ## Choices
27
+
28
+ 対話式の CLI は、次の順に確認します。
29
+
30
+ | 質問 | 選択肢 | 推奨 |
31
+ | --- | --- | --- |
32
+ | Project name | 作るフォルダ名(空なら既定の名前) | 任意。例: `my-site` |
33
+ | Preset | `starter`、`minimal`、`showcase`、`empty` | 大半のサイトは `starter` |
34
+ | Content source | `This project`、`Separate GitHub repository` | 最初は `This project` |
35
+ | デプロイ設定 | `GitHub Actions + Cloudflare Workers`、`Not now` | 手元で試すなら `Not now` |
36
+
37
+ `Separate GitHub repository` を選ぶと content と site のリポジトリ名も入力し、GitHub Actions のデプロイ設定が自動で構成されます。この構成は[content repository ガイド](../../docs/ja/docs/guides/content-repositories.md)に、デプロイの追加方法は [Deployment](../../docs/ja/docs/getting-started/deployment.md) にあります。
38
+
17
39
  ## オプション
18
40
 
19
41
  | オプション | 説明 |
@@ -32,6 +54,8 @@ npm exec riebeckite build
32
54
  npx create-riebeckite my-site --preset showcase
33
55
  ```
34
56
 
57
+ ## External content repository
58
+
35
59
  別の記事リポジトリを使い、その `main` への push でデプロイする場合は、共通の
36
60
  workflow を一度だけ生成します。preset による違いはありません。
37
61
 
@@ -63,7 +87,7 @@ npx create-riebeckite --list-presets
63
87
  | --- | --- |
64
88
  | `starter` | 大半のサイトにおすすめ |
65
89
  | `minimal` | 最小限の設定で作る Markdown サイト |
66
- | `showcase` | 描画例とローカルのフィクスチャで Riebeckite の機能と Plugin を確認する構成 |
90
+ | `showcase` | 描画例とローカルの fixture で Riebeckite の機能と Plugin を確認する構成 |
67
91
  | `empty` | 独自構成のための空のシェル |
68
92
 
69
93
  各 preset の説明は次のとおりです(`--list-presets` で表示される内容と同じです)。
@@ -0,0 +1,13 @@
1
+ import { type ScaffoldPresetName } from "@riebeckite/honox";
2
+ export type CreateRiebeckiteOptions = {
3
+ readonly directory: string;
4
+ readonly force: boolean;
5
+ readonly preset: ScaffoldPresetName;
6
+ readonly listPresets: boolean;
7
+ readonly githubActions: boolean;
8
+ readonly cloudflareWorkers: boolean;
9
+ readonly contentRepository?: string;
10
+ readonly siteRepository?: string;
11
+ };
12
+ export declare function parseArguments(arguments_: readonly string[]): CreateRiebeckiteOptions;
13
+ export declare function printPresets(): void;
@@ -1,11 +1,2 @@
1
- import { type ScaffoldPresetName } from "@riebeckite/honox";
2
- export type CreateRiebeckiteOptions = {
3
- readonly directory: string;
4
- readonly force: boolean;
5
- readonly preset: ScaffoldPresetName;
6
- readonly listPresets: boolean;
7
- readonly githubActions: boolean;
8
- readonly contentRepository?: string;
9
- readonly siteRepository?: string;
10
- };
1
+ export type { CreateRiebeckiteOptions } from "./arguments.js";
11
2
  export declare function runCreateRiebeckite(arguments_: readonly string[]): Promise<void>;
package/dist/src/index.js CHANGED
@@ -2,54 +2,16 @@
2
2
  import path from "node:path";
3
3
  import {
4
4
  formatScaffoldNextSteps,
5
- isScaffoldPresetName,
6
- SCAFFOLD_PRESET_NAMES,
7
5
  ScaffoldSiteError,
8
- scaffoldPresets,
9
6
  scaffoldRiebeckiteSite
10
7
  } from "@riebeckite/honox";
11
- async function runCreateRiebeckite(arguments_) {
12
- let options;
13
- try {
14
- options = parseArguments(arguments_);
15
- } catch (error) {
16
- console.error(error instanceof Error ? error.message : String(error));
17
- process.exitCode = 1;
18
- return;
19
- }
20
- if (options.listPresets) {
21
- printPresets();
22
- return;
23
- }
24
- const targetDirectory = path.resolve(process.cwd(), options.directory);
25
- try {
26
- const result = await scaffoldRiebeckiteSite({
27
- targetDirectory,
28
- overwrite: options.force,
29
- preset: options.preset,
30
- githubActions: options.githubActions,
31
- contentRepository: options.contentRepository,
32
- siteRepository: options.siteRepository
33
- });
34
- const relative = path.relative(process.cwd(), result.targetDirectory) || ".";
35
- console.log(`Created a ${options.preset} Riebeckite site in ${relative}`);
36
- console.log("");
37
- console.log(formatScaffoldNextSteps(relative));
38
- if (options.contentRepository !== void 0) {
39
- console.log("");
40
- console.log(
41
- "Copy github/notify-site.yml to the content repository as .github/workflows/notify-site.yml."
42
- );
43
- }
44
- } catch (error) {
45
- if (error instanceof ScaffoldSiteError) {
46
- console.error(error.message);
47
- process.exitCode = 1;
48
- return;
49
- }
50
- throw error;
51
- }
52
- }
8
+
9
+ // src/arguments.ts
10
+ import {
11
+ isScaffoldPresetName,
12
+ SCAFFOLD_PRESET_NAMES,
13
+ scaffoldPresets
14
+ } from "@riebeckite/honox";
53
15
  function parseArguments(arguments_) {
54
16
  let directory;
55
17
  let force = false;
@@ -109,6 +71,7 @@ function parseArguments(arguments_) {
109
71
  preset: preset ?? "starter",
110
72
  listPresets,
111
73
  githubActions,
74
+ cloudflareWorkers: false,
112
75
  contentRepository,
113
76
  siteRepository
114
77
  };
@@ -120,6 +83,316 @@ function printPresets() {
120
83
  console.log(` ${name}: ${scaffoldPresets[name].description}`);
121
84
  }
122
85
  }
86
+
87
+ // src/interactive.ts
88
+ import { isCancel, log, select, text } from "@clack/prompts";
89
+ import {
90
+ SCAFFOLD_PRESET_NAMES as SCAFFOLD_PRESET_NAMES2,
91
+ scaffoldPresets as scaffoldPresets2
92
+ } from "@riebeckite/honox";
93
+ var DEFAULT_DIRECTORY = "my-riebeckite-site";
94
+ var DEFAULT_INTERACTIVE_DEPLOYMENT = "none";
95
+ function interactiveAnswersToOptions(answers) {
96
+ const external = answers.contentSource === "external";
97
+ return {
98
+ directory: answers.directory,
99
+ force: false,
100
+ preset: answers.preset,
101
+ listPresets: false,
102
+ githubActions: external || answers.deployment === "github-actions",
103
+ cloudflareWorkers: !external && answers.deployment === "cloudflare",
104
+ contentRepository: external ? answers.contentRepository : void 0,
105
+ siteRepository: external ? answers.siteRepository : void 0
106
+ };
107
+ }
108
+ async function promptInteractiveAnswers() {
109
+ const projectInput = await text({
110
+ message: "Project name",
111
+ placeholder: DEFAULT_DIRECTORY,
112
+ defaultValue: DEFAULT_DIRECTORY
113
+ });
114
+ if (isCancel(projectInput)) return null;
115
+ const directory = projectInput.trim() || DEFAULT_DIRECTORY;
116
+ const preset = await select({
117
+ message: "Choose a preset",
118
+ initialValue: "starter",
119
+ options: SCAFFOLD_PRESET_NAMES2.map((name) => ({
120
+ value: name,
121
+ label: capitalize(name),
122
+ hint: scaffoldPresets2[name].description
123
+ }))
124
+ });
125
+ if (isCancel(preset)) return null;
126
+ const contentSource = await select({
127
+ message: "Where will you write content?",
128
+ initialValue: "local",
129
+ options: [
130
+ { value: "local", label: "This project" },
131
+ { value: "external", label: "Separate GitHub repository" }
132
+ ]
133
+ });
134
+ if (isCancel(contentSource)) return null;
135
+ let answers;
136
+ if (contentSource === "external") {
137
+ log.info(
138
+ "Separate repositories use GitHub Actions so content updates can trigger site deployments."
139
+ );
140
+ const contentRepository = await text({
141
+ message: "Content repository",
142
+ placeholder: "OWNER/notes"
143
+ });
144
+ if (isCancel(contentRepository)) return null;
145
+ const siteRepository = await text({
146
+ message: "Site repository",
147
+ placeholder: "OWNER/site"
148
+ });
149
+ if (isCancel(siteRepository)) return null;
150
+ answers = {
151
+ directory,
152
+ preset,
153
+ contentSource,
154
+ deployment: "github-actions",
155
+ contentRepository: contentRepository.trim(),
156
+ siteRepository: siteRepository.trim()
157
+ };
158
+ } else {
159
+ const deployment = await select({
160
+ message: "Set up deployment?",
161
+ initialValue: DEFAULT_INTERACTIVE_DEPLOYMENT,
162
+ options: [
163
+ {
164
+ value: "cloudflare",
165
+ label: "Cloudflare Workers",
166
+ hint: "Publish from this machine"
167
+ },
168
+ {
169
+ value: "github-actions",
170
+ label: "GitHub Actions",
171
+ hint: "Deploy on every push"
172
+ },
173
+ { value: "none", label: "Not now" }
174
+ ]
175
+ });
176
+ if (isCancel(deployment)) return null;
177
+ answers = { directory, preset, contentSource, deployment };
178
+ }
179
+ log.step("Creating Riebeckite site...");
180
+ return answers;
181
+ }
182
+ async function promptDeployNow() {
183
+ const answer = await select({
184
+ message: "Deploy now?",
185
+ initialValue: "yes",
186
+ options: [
187
+ { value: "yes", label: "Yes" },
188
+ { value: "later", label: "Later" }
189
+ ]
190
+ });
191
+ if (isCancel(answer)) return false;
192
+ return answer === "yes";
193
+ }
194
+ function capitalize(value) {
195
+ return value.charAt(0).toUpperCase() + value.slice(1);
196
+ }
197
+
198
+ // src/package-manager.ts
199
+ import { PACKAGE_MANAGER } from "@riebeckite/honox";
200
+ function packageManagerCommands(name = PACKAGE_MANAGER) {
201
+ if (name === "pnpm") {
202
+ return {
203
+ name,
204
+ install: { command: "pnpm", args: ["install"], label: "pnpm install" },
205
+ build: { command: "pnpm", args: ["build"], label: "pnpm build" },
206
+ deploy: {
207
+ command: "pnpm",
208
+ args: ["exec", "riebeckite", "deploy"],
209
+ label: "pnpm exec riebeckite deploy"
210
+ }
211
+ };
212
+ }
213
+ return {
214
+ name: "npm",
215
+ install: { command: "npm", args: ["install"], label: "npm install" },
216
+ build: { command: "npm", args: ["run", "build"], label: "npm run build" },
217
+ deploy: {
218
+ command: "npm",
219
+ args: ["exec", "riebeckite", "deploy"],
220
+ label: "npm exec riebeckite deploy"
221
+ }
222
+ };
223
+ }
224
+
225
+ // src/publish.ts
226
+ var DeploymentFailure = class extends Error {
227
+ phase;
228
+ constructor(phase, message) {
229
+ super(message);
230
+ this.name = "DeploymentFailure";
231
+ this.phase = phase;
232
+ }
233
+ };
234
+ async function installDependencies(directory, commands, run) {
235
+ await runCommand("install", commands.install, directory, run);
236
+ }
237
+ async function buildAndDeploy(directory, commands, run) {
238
+ await runCommand("build", commands.build, directory, run);
239
+ await runCommand("deploy", commands.deploy, directory, run);
240
+ }
241
+ function formatCloudflareNextSteps(relativeDirectory, commands) {
242
+ const lines = ["Next steps:"];
243
+ if (relativeDirectory !== ".") {
244
+ lines.push(` cd ${relativeDirectory}`);
245
+ }
246
+ lines.push(` ${commands.build.label}`);
247
+ lines.push(` ${commands.deploy.label}`);
248
+ return lines.join("\n");
249
+ }
250
+ function formatDeploymentFailure(relativeDirectory, commands, failure) {
251
+ const lines = [
252
+ "Project created successfully.",
253
+ deploymentFailureSummary(failure.phase),
254
+ failure.message,
255
+ "You can retry with:"
256
+ ];
257
+ if (relativeDirectory !== ".") {
258
+ lines.push(` cd ${relativeDirectory}`);
259
+ }
260
+ if (failure.phase === "install") {
261
+ lines.push(` ${commands.install.label}`);
262
+ }
263
+ lines.push(` ${commands.build.label}`);
264
+ lines.push(` ${commands.deploy.label}`);
265
+ return lines.join("\n");
266
+ }
267
+ function deploymentFailureSummary(phase) {
268
+ if (phase === "install") return "Installing dependencies failed.";
269
+ if (phase === "build") return "Build failed.";
270
+ return "Deployment failed.";
271
+ }
272
+ async function runCommand(phase, command, directory, run) {
273
+ let exitCode;
274
+ try {
275
+ exitCode = await run(command.command, command.args, directory);
276
+ } catch (error) {
277
+ throw new DeploymentFailure(
278
+ phase,
279
+ error instanceof Error ? error.message : String(error)
280
+ );
281
+ }
282
+ if (exitCode !== 0) {
283
+ throw new DeploymentFailure(
284
+ phase,
285
+ `${command.label} exited with code ${exitCode}.`
286
+ );
287
+ }
288
+ }
289
+
290
+ // src/run-commands.ts
291
+ import { spawn } from "node:child_process";
292
+ function spawnCommand(command, args, cwd) {
293
+ return new Promise((resolve, reject) => {
294
+ const child = spawn(command, [...args], {
295
+ cwd,
296
+ stdio: "inherit",
297
+ shell: process.platform === "win32"
298
+ });
299
+ child.on("error", reject);
300
+ child.on("exit", (code) => resolve(code ?? 1));
301
+ });
302
+ }
303
+
304
+ // src/index.ts
305
+ async function runCreateRiebeckite(arguments_) {
306
+ const resolved = await resolveOptions(arguments_);
307
+ if (resolved === null) return;
308
+ const { options, interactive } = resolved;
309
+ if (options.listPresets) {
310
+ printPresets();
311
+ return;
312
+ }
313
+ const targetDirectory = path.resolve(process.cwd(), options.directory);
314
+ let result;
315
+ try {
316
+ result = await scaffoldRiebeckiteSite({
317
+ targetDirectory,
318
+ overwrite: options.force,
319
+ preset: options.preset,
320
+ githubActions: options.githubActions,
321
+ cloudflareWorkers: options.cloudflareWorkers,
322
+ contentRepository: options.contentRepository,
323
+ siteRepository: options.siteRepository
324
+ });
325
+ } catch (error) {
326
+ if (error instanceof ScaffoldSiteError) {
327
+ console.error(error.message);
328
+ process.exitCode = 1;
329
+ return;
330
+ }
331
+ throw error;
332
+ }
333
+ const relative = path.relative(process.cwd(), result.targetDirectory) || ".";
334
+ console.log(`Created a ${options.preset} Riebeckite site in ${relative}`);
335
+ console.log("");
336
+ if (interactive && options.cloudflareWorkers) {
337
+ await publishCloudflareSite(targetDirectory, relative);
338
+ return;
339
+ }
340
+ printScaffoldNextSteps(options, result, relative);
341
+ }
342
+ async function resolveOptions(arguments_) {
343
+ if (arguments_.length === 0) {
344
+ const answers = await promptInteractiveAnswers();
345
+ if (answers === null) {
346
+ console.log("Operation cancelled.");
347
+ return null;
348
+ }
349
+ return { options: interactiveAnswersToOptions(answers), interactive: true };
350
+ }
351
+ try {
352
+ return { options: parseArguments(arguments_), interactive: false };
353
+ } catch (error) {
354
+ console.error(error instanceof Error ? error.message : String(error));
355
+ process.exitCode = 1;
356
+ return null;
357
+ }
358
+ }
359
+ function printScaffoldNextSteps(options, result, relative) {
360
+ const externalContent = options.contentRepository !== void 0;
361
+ const editFile = !externalContent && result.files.includes("content/index.md") ? "content/index.md" : void 0;
362
+ console.log(formatScaffoldNextSteps(relative, { editFile, externalContent }));
363
+ }
364
+ async function publishCloudflareSite(targetDirectory, relative) {
365
+ const commands = packageManagerCommands();
366
+ console.log(`Installing dependencies with ${commands.name}...`);
367
+ try {
368
+ await installDependencies(targetDirectory, commands, spawnCommand);
369
+ } catch (error) {
370
+ if (!(error instanceof DeploymentFailure)) throw error;
371
+ reportDeploymentFailure(error, relative);
372
+ return;
373
+ }
374
+ const deployNow = await promptDeployNow();
375
+ if (!deployNow) {
376
+ console.log("");
377
+ console.log(formatCloudflareNextSteps(relative, commands));
378
+ return;
379
+ }
380
+ try {
381
+ await buildAndDeploy(targetDirectory, commands, spawnCommand);
382
+ } catch (error) {
383
+ if (!(error instanceof DeploymentFailure)) throw error;
384
+ reportDeploymentFailure(error, relative);
385
+ return;
386
+ }
387
+ console.log("");
388
+ console.log("Deployment complete.");
389
+ }
390
+ function reportDeploymentFailure(error, relative) {
391
+ const commands = packageManagerCommands();
392
+ console.error("");
393
+ console.error(formatDeploymentFailure(relative, commands, error));
394
+ process.exitCode = 1;
395
+ }
123
396
  export {
124
397
  runCreateRiebeckite
125
398
  };
@@ -0,0 +1,26 @@
1
+ import { type ScaffoldPresetName } from "@riebeckite/honox";
2
+ import type { CreateRiebeckiteOptions } from "./arguments.js";
3
+ export declare const DEFAULT_INTERACTIVE_DEPLOYMENT: InteractiveDeployment;
4
+ export type InteractiveContentSource = "local" | "external";
5
+ export type InteractiveDeployment = "cloudflare" | "github-actions" | "none";
6
+ export type InteractiveAnswers = {
7
+ readonly directory: string;
8
+ readonly preset: ScaffoldPresetName;
9
+ readonly contentSource: InteractiveContentSource;
10
+ readonly deployment: InteractiveDeployment;
11
+ readonly contentRepository?: string;
12
+ readonly siteRepository?: string;
13
+ };
14
+ /**
15
+ * Map interactive answers onto the same options object the non-interactive
16
+ * flags produce. A separate content repository always deploys through GitHub
17
+ * Actions, so the answered deployment never wins in that combination.
18
+ */
19
+ export declare function interactiveAnswersToOptions(answers: InteractiveAnswers): CreateRiebeckiteOptions;
20
+ /**
21
+ * Run the prompt flow. Returns `null` when the user cancels, so the caller can
22
+ * exit without a stack trace. Repository values are only trimmed here; their
23
+ * `owner/repository` shape is validated by the existing scaffold.
24
+ */
25
+ export declare function promptInteractiveAnswers(): Promise<InteractiveAnswers | null>;
26
+ export declare function promptDeployNow(): Promise<boolean>;
@@ -0,0 +1,13 @@
1
+ export type PackageManagerName = "npm" | "pnpm";
2
+ export type PackageManagerCommand = {
3
+ readonly command: string;
4
+ readonly args: readonly string[];
5
+ readonly label: string;
6
+ };
7
+ export type PackageManagerCommands = {
8
+ readonly name: PackageManagerName;
9
+ readonly install: PackageManagerCommand;
10
+ readonly build: PackageManagerCommand;
11
+ readonly deploy: PackageManagerCommand;
12
+ };
13
+ export declare function packageManagerCommands(name?: PackageManagerName): PackageManagerCommands;
@@ -0,0 +1,11 @@
1
+ import type { PackageManagerCommands } from "./package-manager.js";
2
+ import type { CommandRunner } from "./run-commands.js";
3
+ export type DeploymentPhase = "install" | "build" | "deploy";
4
+ export declare class DeploymentFailure extends Error {
5
+ readonly phase: DeploymentPhase;
6
+ constructor(phase: DeploymentPhase, message: string);
7
+ }
8
+ export declare function installDependencies(directory: string, commands: PackageManagerCommands, run: CommandRunner): Promise<void>;
9
+ export declare function buildAndDeploy(directory: string, commands: PackageManagerCommands, run: CommandRunner): Promise<void>;
10
+ export declare function formatCloudflareNextSteps(relativeDirectory: string, commands: PackageManagerCommands): string;
11
+ export declare function formatDeploymentFailure(relativeDirectory: string, commands: PackageManagerCommands, failure: DeploymentFailure): string;
@@ -0,0 +1,2 @@
1
+ export type CommandRunner = (command: string, args: readonly string[], cwd: string) => Promise<number>;
2
+ export declare function spawnCommand(command: string, args: readonly string[], cwd: string): Promise<number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-riebeckite",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -27,7 +27,8 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@riebeckite/honox": "0.0.14"
30
+ "@clack/prompts": "^1.8.1",
31
+ "@riebeckite/honox": "0.0.16"
31
32
  },
32
33
  "repository": {
33
34
  "type": "git",
@@ -50,6 +51,7 @@
50
51
  },
51
52
  "sideEffects": false,
52
53
  "scripts": {
53
- "build": "node ../../scripts/build_package.mjs"
54
+ "build": "node ../../scripts/build_package.mjs",
55
+ "test": "node --import tsx --test \"test/*.test.ts\""
54
56
  }
55
57
  }