@reckona/create-mreact-app 0.0.94 → 0.0.95

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.
@@ -0,0 +1,75 @@
1
+ import { createMreactAppDeployTargets, createMreactAppTemplates } from "./index.js";
2
+ import { select, text } from "./prompts.js";
3
+ const DEFAULT_DIRECTORY = "mreact-app";
4
+ const DEFAULT_TEMPLATE = "basic";
5
+ const DEFAULT_PACKAGE_MANAGER = "pnpm";
6
+ const PACKAGE_MANAGERS = ["pnpm", "npm", "bun"];
7
+ /** Identify the package manager that invoked us via `npm_config_user_agent`. */
8
+ export function detectPackageManager(env) {
9
+ const userAgent = env.npm_config_user_agent;
10
+ if (userAgent === undefined) {
11
+ return undefined;
12
+ }
13
+ const name = userAgent.split("/")[0];
14
+ if (name === "pnpm" || name === "npm" || name === "bun") {
15
+ return name;
16
+ }
17
+ return undefined;
18
+ }
19
+ /**
20
+ * Resolve a complete set of scaffolding options from parsed CLI flags.
21
+ *
22
+ * Non-interactive (non-TTY): every unprovided option falls back to its
23
+ * default. Interactive (TTY): only the unprovided options are prompted for.
24
+ */
25
+ export async function resolveCreateOptions(parsed, context) {
26
+ const env = context.env ?? process.env;
27
+ const detectedPackageManager = detectPackageManager(env);
28
+ const packageManagerDefault = detectedPackageManager ?? DEFAULT_PACKAGE_MANAGER;
29
+ if (context.isTTY !== true) {
30
+ return {
31
+ deploy: parsed.deploy,
32
+ directory: parsed.directory ?? DEFAULT_DIRECTORY,
33
+ packageManager: parsed.packageManager ?? packageManagerDefault,
34
+ srcDir: parsed.srcDir ?? false,
35
+ template: parsed.template ?? DEFAULT_TEMPLATE,
36
+ };
37
+ }
38
+ const streams = { input: context.input, output: context.output };
39
+ const directory = parsed.directory ??
40
+ (await text({ defaultValue: DEFAULT_DIRECTORY, message: "Project directory", ...streams }));
41
+ const template = parsed.template ??
42
+ (await select({
43
+ choices: createMreactAppTemplates.map((value) => ({ label: value, value })),
44
+ initialIndex: createMreactAppTemplates.indexOf(DEFAULT_TEMPLATE),
45
+ message: "Template",
46
+ ...streams,
47
+ }));
48
+ const packageManager = parsed.packageManager ??
49
+ (await select({
50
+ choices: PACKAGE_MANAGERS.map((value) => ({ label: value, value })),
51
+ initialIndex: PACKAGE_MANAGERS.indexOf(packageManagerDefault),
52
+ message: "Package manager",
53
+ ...streams,
54
+ }));
55
+ const srcDir = parsed.srcDir ??
56
+ (await select({
57
+ choices: [
58
+ { label: "No", value: false },
59
+ { label: "Yes", value: true },
60
+ ],
61
+ message: "Place routes under src/app?",
62
+ ...streams,
63
+ }));
64
+ const deploy = parsed.deploy ??
65
+ (await select({
66
+ choices: [
67
+ { label: "none", value: undefined },
68
+ ...createMreactAppDeployTargets.map((value) => ({ label: value, value })),
69
+ ],
70
+ message: "Deploy target",
71
+ ...streams,
72
+ }));
73
+ return { deploy, directory, packageManager, srcDir, template };
74
+ }
75
+ //# sourceMappingURL=resolve-create-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-create-options.js","sourceRoot":"","sources":["../src/resolve-create-options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,4BAA4B,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AA2B5C,MAAM,iBAAiB,GAAG,YAAY,CAAC;AACvC,MAAM,gBAAgB,GAA4B,OAAO,CAAC;AAC1D,MAAM,uBAAuB,GAAkC,MAAM,CAAC;AAEtE,MAAM,gBAAgB,GAA6C,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAE1F,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAClC,GAAsB;IAEtB,MAAM,SAAS,GAAG,GAAG,CAAC,qBAAqB,CAAC;IAC5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAuC,EACvC,OAAoC;IAEpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACzD,MAAM,qBAAqB,GAAG,sBAAsB,IAAI,uBAAuB,CAAC;IAEhF,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,iBAAiB;YAChD,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,qBAAqB;YAC9D,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,KAAK;YAC9B,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,gBAAgB;SAC9C,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAEjE,MAAM,SAAS,GACb,MAAM,CAAC,SAAS;QAChB,CAAC,MAAM,IAAI,CAAC,EAAE,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;IAE9F,MAAM,QAAQ,GACZ,MAAM,CAAC,QAAQ;QACf,CAAC,MAAM,MAAM,CAA0B;YACrC,OAAO,EAAE,wBAAwB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3E,YAAY,EAAE,wBAAwB,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAChE,OAAO,EAAE,UAAU;YACnB,GAAG,OAAO;SACX,CAAC,CAAC,CAAC;IAEN,MAAM,cAAc,GAClB,MAAM,CAAC,cAAc;QACrB,CAAC,MAAM,MAAM,CAAgC;YAC3C,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACnE,YAAY,EAAE,gBAAgB,CAAC,OAAO,CAAC,qBAAqB,CAAC;YAC7D,OAAO,EAAE,iBAAiB;YAC1B,GAAG,OAAO;SACX,CAAC,CAAC,CAAC;IAEN,MAAM,MAAM,GACV,MAAM,CAAC,MAAM;QACb,CAAC,MAAM,MAAM,CAAU;YACrB,OAAO,EAAE;gBACP,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;gBAC7B,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;aAC9B;YACD,OAAO,EAAE,6BAA6B;YACtC,GAAG,OAAO;SACX,CAAC,CAAC,CAAC;IAEN,MAAM,MAAM,GACV,MAAM,CAAC,MAAM;QACb,CAAC,MAAM,MAAM,CAA0C;YACrD,OAAO,EAAE;gBACP,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE;gBACnC,GAAG,4BAA4B,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;aAC1E;YACD,OAAO,EAAE,eAAe;YACxB,GAAG,OAAO;SACX,CAAC,CAAC,CAAC;IAEN,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACjE,CAAC","sourcesContent":["import type { CreateMreactAppCreateCliOptions } from \"./cli-args.js\";\nimport type {\n CreateMreactAppDeployTarget,\n CreateMreactAppPackageManager,\n CreateMreactAppTemplate,\n} from \"./index.js\";\nimport { createMreactAppDeployTargets, createMreactAppTemplates } from \"./index.js\";\nimport { select, text } from \"./prompts.js\";\n\ninterface PromptStreamControls {\n isTTY?: boolean;\n pause?: () => void;\n resume?: () => void;\n setRawMode?: (mode: boolean) => void;\n}\n\ntype PromptReadable = NodeJS.ReadableStream & PromptStreamControls;\ntype PromptWritable = NodeJS.WritableStream;\n\nexport interface ResolveCreateOptionsContext {\n env?: NodeJS.ProcessEnv | undefined;\n input?: PromptReadable | undefined;\n isTTY?: boolean | undefined;\n output?: PromptWritable | undefined;\n}\n\nexport interface ResolvedCreateOptions {\n deploy?: CreateMreactAppDeployTarget | undefined;\n directory: string;\n packageManager: CreateMreactAppPackageManager;\n srcDir: boolean;\n template: CreateMreactAppTemplate;\n}\n\nconst DEFAULT_DIRECTORY = \"mreact-app\";\nconst DEFAULT_TEMPLATE: CreateMreactAppTemplate = \"basic\";\nconst DEFAULT_PACKAGE_MANAGER: CreateMreactAppPackageManager = \"pnpm\";\n\nconst PACKAGE_MANAGERS: readonly CreateMreactAppPackageManager[] = [\"pnpm\", \"npm\", \"bun\"];\n\n/** Identify the package manager that invoked us via `npm_config_user_agent`. */\nexport function detectPackageManager(\n env: NodeJS.ProcessEnv,\n): CreateMreactAppPackageManager | undefined {\n const userAgent = env.npm_config_user_agent;\n if (userAgent === undefined) {\n return undefined;\n }\n\n const name = userAgent.split(\"/\")[0];\n if (name === \"pnpm\" || name === \"npm\" || name === \"bun\") {\n return name;\n }\n\n return undefined;\n}\n\n/**\n * Resolve a complete set of scaffolding options from parsed CLI flags.\n *\n * Non-interactive (non-TTY): every unprovided option falls back to its\n * default. Interactive (TTY): only the unprovided options are prompted for.\n */\nexport async function resolveCreateOptions(\n parsed: CreateMreactAppCreateCliOptions,\n context: ResolveCreateOptionsContext,\n): Promise<ResolvedCreateOptions> {\n const env = context.env ?? process.env;\n const detectedPackageManager = detectPackageManager(env);\n const packageManagerDefault = detectedPackageManager ?? DEFAULT_PACKAGE_MANAGER;\n\n if (context.isTTY !== true) {\n return {\n deploy: parsed.deploy,\n directory: parsed.directory ?? DEFAULT_DIRECTORY,\n packageManager: parsed.packageManager ?? packageManagerDefault,\n srcDir: parsed.srcDir ?? false,\n template: parsed.template ?? DEFAULT_TEMPLATE,\n };\n }\n\n const streams = { input: context.input, output: context.output };\n\n const directory =\n parsed.directory ??\n (await text({ defaultValue: DEFAULT_DIRECTORY, message: \"Project directory\", ...streams }));\n\n const template =\n parsed.template ??\n (await select<CreateMreactAppTemplate>({\n choices: createMreactAppTemplates.map((value) => ({ label: value, value })),\n initialIndex: createMreactAppTemplates.indexOf(DEFAULT_TEMPLATE),\n message: \"Template\",\n ...streams,\n }));\n\n const packageManager =\n parsed.packageManager ??\n (await select<CreateMreactAppPackageManager>({\n choices: PACKAGE_MANAGERS.map((value) => ({ label: value, value })),\n initialIndex: PACKAGE_MANAGERS.indexOf(packageManagerDefault),\n message: \"Package manager\",\n ...streams,\n }));\n\n const srcDir =\n parsed.srcDir ??\n (await select<boolean>({\n choices: [\n { label: \"No\", value: false },\n { label: \"Yes\", value: true },\n ],\n message: \"Place routes under src/app?\",\n ...streams,\n }));\n\n const deploy =\n parsed.deploy ??\n (await select<CreateMreactAppDeployTarget | undefined>({\n choices: [\n { label: \"none\", value: undefined },\n ...createMreactAppDeployTargets.map((value) => ({ label: value, value })),\n ],\n message: \"Deploy target\",\n ...streams,\n }));\n\n return { deploy, directory, packageManager, srcDir, template };\n}\n"]}
@@ -0,0 +1,29 @@
1
+ interface PromptStreamControls {
2
+ isTTY?: boolean;
3
+ pause?: () => void;
4
+ resume?: () => void;
5
+ setRawMode?: (mode: boolean) => void;
6
+ }
7
+ export interface RunCliContext {
8
+ /** Environment used for package-manager detection. Defaults to `process.env`. */
9
+ env?: NodeJS.ProcessEnv | undefined;
10
+ /** Stream the interactive prompts read from. Defaults to `process.stdin`. */
11
+ input?: (NodeJS.ReadableStream & PromptStreamControls) | undefined;
12
+ /** Whether to run the interactive wizard for unprovided options. */
13
+ isTTY?: boolean | undefined;
14
+ /** Stream the interactive prompts render to. Defaults to `process.stdout`. */
15
+ output?: NodeJS.WritableStream | undefined;
16
+ /** Sink for error messages. Defaults to `console.error`. */
17
+ stderr?: ((line: string) => void) | undefined;
18
+ /** Sink for normal output. Defaults to `console.log`. */
19
+ stdout?: ((line: string) => void) | undefined;
20
+ }
21
+ /**
22
+ * Run the create-mreact-app CLI and return a process exit code.
23
+ *
24
+ * Pulled out of the bin shim so the full flow (parse -> resolve -> scaffold)
25
+ * is testable with in-memory streams and without touching `process`.
26
+ */
27
+ export declare function runCreateMreactAppCli(argv: readonly string[], context?: RunCliContext): Promise<number>;
28
+ export {};
29
+ //# sourceMappingURL=run-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAUA,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IACpC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,GAAG,oBAAoB,CAAC,GAAG,SAAS,CAAC;IACnE,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG,SAAS,CAAC;IAC3C,4DAA4D;IAC5D,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC9C,yDAAyD;IACzD,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC/C;AAMD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiEjB"}
@@ -0,0 +1,71 @@
1
+ import { resolve } from "node:path";
2
+ import { createMreactAppHelpText, createMreactAppSuccessText, parseCreateMreactAppCliArgs, } from "./cli-args.js";
3
+ import { createMreactApp, upgradeMreactApp } from "./index.js";
4
+ import { isPromptCancelled } from "./prompts.js";
5
+ import { resolveCreateOptions } from "./resolve-create-options.js";
6
+ const EXIT_OK = 0;
7
+ const EXIT_ERROR = 1;
8
+ const EXIT_CANCELLED = 130;
9
+ /**
10
+ * Run the create-mreact-app CLI and return a process exit code.
11
+ *
12
+ * Pulled out of the bin shim so the full flow (parse -> resolve -> scaffold)
13
+ * is testable with in-memory streams and without touching `process`.
14
+ */
15
+ export async function runCreateMreactAppCli(argv, context = {}) {
16
+ const stdout = context.stdout ?? ((line) => console.log(line));
17
+ const stderr = context.stderr ?? ((line) => console.error(line));
18
+ try {
19
+ const options = parseCreateMreactAppCliArgs(argv);
20
+ if (options.help === true) {
21
+ stdout(createMreactAppHelpText());
22
+ return EXIT_OK;
23
+ }
24
+ if (options.command === "upgrade") {
25
+ const result = await upgradeMreactApp({
26
+ directory: resolve(options.directory ?? "."),
27
+ dryRun: options.dryRun,
28
+ fromVersion: options.fromVersion,
29
+ targetVersion: options.targetVersion,
30
+ });
31
+ stdout(result.changed
32
+ ? `Updated ${result.updatedDependencies.length} mreact dependency range(s).`
33
+ : "No mreact dependency ranges needed updating.");
34
+ if (result.codemods.length > 0) {
35
+ stdout(`Codemods: ${result.codemods.map((codemod) => codemod.id).join(", ")}`);
36
+ }
37
+ return EXIT_OK;
38
+ }
39
+ const resolved = await resolveCreateOptions(options, {
40
+ env: context.env ?? process.env,
41
+ input: context.input,
42
+ isTTY: context.isTTY,
43
+ output: context.output,
44
+ });
45
+ const result = await createMreactApp({
46
+ deploy: resolved.deploy,
47
+ directory: resolve(resolved.directory),
48
+ // Leave `name` unset so createMreactApp derives a workspace-aware package
49
+ // name from the target directory (e.g. @reckona/example-<name> under examples/*).
50
+ packageManager: resolved.packageManager,
51
+ srcDir: resolved.srcDir,
52
+ template: resolved.template,
53
+ });
54
+ stdout(createMreactAppSuccessText({
55
+ directory: result.directory,
56
+ displayDirectory: resolved.directory,
57
+ packageManager: result.packageManager,
58
+ template: result.template,
59
+ }));
60
+ return EXIT_OK;
61
+ }
62
+ catch (error) {
63
+ if (isPromptCancelled(error)) {
64
+ stderr("Aborted.");
65
+ return EXIT_CANCELLED;
66
+ }
67
+ stderr(error instanceof Error ? error.message : String(error));
68
+ return EXIT_ERROR;
69
+ }
70
+ }
71
+ //# sourceMappingURL=run-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-cli.js","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport {\n createMreactAppHelpText,\n createMreactAppSuccessText,\n parseCreateMreactAppCliArgs,\n} from \"./cli-args.js\";\nimport { createMreactApp, upgradeMreactApp } from \"./index.js\";\nimport { isPromptCancelled } from \"./prompts.js\";\nimport { resolveCreateOptions } from \"./resolve-create-options.js\";\n\ninterface PromptStreamControls {\n isTTY?: boolean;\n pause?: () => void;\n resume?: () => void;\n setRawMode?: (mode: boolean) => void;\n}\n\nexport interface RunCliContext {\n /** Environment used for package-manager detection. Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv | undefined;\n /** Stream the interactive prompts read from. Defaults to `process.stdin`. */\n input?: (NodeJS.ReadableStream & PromptStreamControls) | undefined;\n /** Whether to run the interactive wizard for unprovided options. */\n isTTY?: boolean | undefined;\n /** Stream the interactive prompts render to. Defaults to `process.stdout`. */\n output?: NodeJS.WritableStream | undefined;\n /** Sink for error messages. Defaults to `console.error`. */\n stderr?: ((line: string) => void) | undefined;\n /** Sink for normal output. Defaults to `console.log`. */\n stdout?: ((line: string) => void) | undefined;\n}\n\nconst EXIT_OK = 0;\nconst EXIT_ERROR = 1;\nconst EXIT_CANCELLED = 130;\n\n/**\n * Run the create-mreact-app CLI and return a process exit code.\n *\n * Pulled out of the bin shim so the full flow (parse -> resolve -> scaffold)\n * is testable with in-memory streams and without touching `process`.\n */\nexport async function runCreateMreactAppCli(\n argv: readonly string[],\n context: RunCliContext = {},\n): Promise<number> {\n const stdout = context.stdout ?? ((line: string) => console.log(line));\n const stderr = context.stderr ?? ((line: string) => console.error(line));\n\n try {\n const options = parseCreateMreactAppCliArgs(argv);\n\n if (options.help === true) {\n stdout(createMreactAppHelpText());\n return EXIT_OK;\n }\n\n if (options.command === \"upgrade\") {\n const result = await upgradeMreactApp({\n directory: resolve(options.directory ?? \".\"),\n dryRun: options.dryRun,\n fromVersion: options.fromVersion,\n targetVersion: options.targetVersion,\n });\n\n stdout(\n result.changed\n ? `Updated ${result.updatedDependencies.length} mreact dependency range(s).`\n : \"No mreact dependency ranges needed updating.\",\n );\n if (result.codemods.length > 0) {\n stdout(`Codemods: ${result.codemods.map((codemod) => codemod.id).join(\", \")}`);\n }\n return EXIT_OK;\n }\n\n const resolved = await resolveCreateOptions(options, {\n env: context.env ?? process.env,\n input: context.input,\n isTTY: context.isTTY,\n output: context.output,\n });\n\n const result = await createMreactApp({\n deploy: resolved.deploy,\n directory: resolve(resolved.directory),\n // Leave `name` unset so createMreactApp derives a workspace-aware package\n // name from the target directory (e.g. @reckona/example-<name> under examples/*).\n packageManager: resolved.packageManager,\n srcDir: resolved.srcDir,\n template: resolved.template,\n });\n\n stdout(\n createMreactAppSuccessText({\n directory: result.directory,\n displayDirectory: resolved.directory,\n packageManager: result.packageManager,\n template: result.template,\n }),\n );\n return EXIT_OK;\n } catch (error) {\n if (isPromptCancelled(error)) {\n stderr(\"Aborted.\");\n return EXIT_CANCELLED;\n }\n stderr(error instanceof Error ? error.message : String(error));\n return EXIT_ERROR;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/create-mreact-app",
3
- "version": "0.0.94",
3
+ "version": "0.0.95",
4
4
  "description": "Project scaffolder for mreact app-router applications.",
5
5
  "keywords": [
6
6
  "cli",
package/src/cli-args.ts CHANGED
@@ -8,11 +8,15 @@ import {
8
8
  export interface CreateMreactAppCreateCliOptions {
9
9
  command: "create";
10
10
  deploy?: CreateMreactAppDeployTarget | undefined;
11
- directory: string;
11
+ /** Undefined when no positional directory was given (the wizard will prompt). */
12
+ directory?: string | undefined;
12
13
  help?: boolean | undefined;
13
- packageManager: CreateMreactAppPackageManager;
14
- srcDir: boolean;
15
- template: CreateMreactAppTemplate;
14
+ /** Undefined when `--pm` was not passed (the wizard will prompt). */
15
+ packageManager?: CreateMreactAppPackageManager | undefined;
16
+ /** Undefined when `--src-dir` was not passed (the wizard will prompt). */
17
+ srcDir?: boolean | undefined;
18
+ /** Undefined when `--template` was not passed (the wizard will prompt). */
19
+ template?: CreateMreactAppTemplate | undefined;
16
20
  }
17
21
 
18
22
  export interface CreateMreactAppUpgradeCliOptions {
@@ -34,10 +38,10 @@ export function parseCreateMreactAppCliArgs(args: readonly string[]): CreateMrea
34
38
  }
35
39
 
36
40
  const directories: string[] = [];
37
- let template: CreateMreactAppTemplate = "app-router";
38
- let packageManager: CreateMreactAppPackageManager = "pnpm";
41
+ let template: CreateMreactAppTemplate | undefined;
42
+ let packageManager: CreateMreactAppPackageManager | undefined;
39
43
  let deploy: CreateMreactAppDeployTarget | undefined;
40
- let srcDir = false;
44
+ let srcDir: boolean | undefined;
41
45
 
42
46
  for (let index = 0; index < args.length; index += 1) {
43
47
  const arg = args[index];
@@ -51,7 +55,7 @@ export function parseCreateMreactAppCliArgs(args: readonly string[]): CreateMrea
51
55
  return {
52
56
  command: "create",
53
57
  deploy,
54
- directory: directories[0] ?? "mreact-app",
58
+ directory: directories[0],
55
59
  help: true,
56
60
  packageManager,
57
61
  srcDir,
@@ -118,7 +122,7 @@ export function parseCreateMreactAppCliArgs(args: readonly string[]): CreateMrea
118
122
  return {
119
123
  command: "create",
120
124
  deploy,
121
- directory: directories[0] ?? "mreact-app",
125
+ directory: directories[0],
122
126
  packageManager,
123
127
  srcDir,
124
128
  template,
@@ -131,10 +135,14 @@ export function createMreactAppHelpText(): string {
131
135
  " create-mreact-app [directory] [options]",
132
136
  " create-mreact-app upgrade [directory] [options]",
133
137
  "",
138
+ "Run in a terminal with options left out and create-mreact-app prompts for",
139
+ "them interactively. With every option supplied (or no TTY) it runs without",
140
+ "prompts, so scripts and CI keep working unchanged.",
141
+ "",
134
142
  "Options:",
135
- ` --template <name> Template to generate: ${createMreactAppTemplates.join(", ")}. Default: app-router.`,
143
+ ` --template <name> App template: ${createMreactAppTemplates.join(", ")}. Default: basic.`,
136
144
  " --pm, --package-manager <pm> Package manager for generated scripts: pnpm, npm, or bun. Default: pnpm.",
137
- " --deploy <target> Add deploy files: container or aws-lambda.",
145
+ " --deploy <target> Deploy target: cloudflare, container, or aws-lambda.",
138
146
  " --src-dir Generate routes under src/app instead of app.",
139
147
  " -h, --help Show this help message.",
140
148
  "",
@@ -145,7 +153,8 @@ export function createMreactAppHelpText(): string {
145
153
  "",
146
154
  "Examples:",
147
155
  " create-mreact-app my-app",
148
- " create-mreact-app my-app --template app-router-tailwind --src-dir",
156
+ " create-mreact-app my-app --template tailwind --src-dir",
157
+ " create-mreact-app my-app --deploy cloudflare",
149
158
  " create-mreact-app my-app --deploy container",
150
159
  " create-mreact-app my-app --deploy aws-lambda",
151
160
  " create-mreact-app upgrade --dry-run",
@@ -273,13 +282,7 @@ function readOptionValue(args: readonly string[], index: number, name: string):
273
282
  }
274
283
 
275
284
  function parseTemplate(value: string | undefined): CreateMreactAppTemplate {
276
- if (
277
- value === "basic" ||
278
- value === "app-router" ||
279
- value === "app-router-tailwind" ||
280
- value === "cloudflare" ||
281
- value === "dashboard"
282
- ) {
285
+ if (value === "basic" || value === "tailwind" || value === "dashboard") {
283
286
  return value;
284
287
  }
285
288
 
@@ -297,9 +300,11 @@ function parsePackageManager(value: string | undefined): CreateMreactAppPackageM
297
300
  }
298
301
 
299
302
  function parseDeployTarget(value: string | undefined): CreateMreactAppDeployTarget {
300
- if (value === "aws-lambda" || value === "container") {
303
+ if (value === "aws-lambda" || value === "cloudflare" || value === "container") {
301
304
  return value;
302
305
  }
303
306
 
304
- throw new Error(`Unknown deploy target ${JSON.stringify(value)}. Use aws-lambda or container.`);
307
+ throw new Error(
308
+ `Unknown deploy target ${JSON.stringify(value)}. Use cloudflare, container, or aws-lambda.`,
309
+ );
305
310
  }
package/src/cli.ts CHANGED
@@ -1,57 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { resolve } from "node:path";
4
- import {
5
- createMreactAppHelpText,
6
- createMreactAppSuccessText,
7
- parseCreateMreactAppCliArgs,
8
- } from "./cli-args.js";
9
- import { createMreactApp, upgradeMreactApp } from "./index.js";
3
+ import { runCreateMreactAppCli } from "./run-cli.js";
10
4
 
11
- try {
12
- const options = parseCreateMreactAppCliArgs(process.argv.slice(2));
13
- if (options.help === true) {
14
- console.log(createMreactAppHelpText());
15
- process.exit(0);
16
- }
5
+ const isTTY = process.stdin.isTTY === true && process.stdout.isTTY === true;
17
6
 
18
- if (options.command === "upgrade") {
19
- const result = await upgradeMreactApp({
20
- directory: resolve(options.directory),
21
- dryRun: options.dryRun,
22
- fromVersion: options.fromVersion,
23
- targetVersion: options.targetVersion,
24
- });
25
-
26
- console.log(
27
- result.changed
28
- ? `Updated ${result.updatedDependencies.length} mreact dependency range(s).`
29
- : "No mreact dependency ranges needed updating.",
30
- );
31
- if (result.codemods.length > 0) {
32
- console.log(`Codemods: ${result.codemods.map((codemod) => codemod.id).join(", ")}`);
33
- }
34
- process.exit(0);
35
- }
36
-
37
- const result = await createMreactApp({
38
- deploy: options.deploy,
39
- directory: resolve(options.directory),
40
- name: options.directory,
41
- packageManager: options.packageManager,
42
- srcDir: options.srcDir,
43
- template: options.template,
44
- });
45
-
46
- console.log(
47
- createMreactAppSuccessText({
48
- directory: result.directory,
49
- displayDirectory: options.directory,
50
- packageManager: result.packageManager,
51
- template: result.template,
52
- }),
53
- );
54
- } catch (error) {
55
- console.error(error instanceof Error ? error.message : String(error));
56
- process.exitCode = 1;
57
- }
7
+ process.exitCode = await runCreateMreactAppCli(process.argv.slice(2), {
8
+ env: process.env,
9
+ input: process.stdin,
10
+ isTTY,
11
+ output: process.stdout,
12
+ });
package/src/index.ts CHANGED
@@ -1,15 +1,10 @@
1
1
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, relative, resolve } from "node:path";
3
3
 
4
- export type CreateMreactAppTemplate =
5
- | "basic"
6
- | "app-router"
7
- | "app-router-tailwind"
8
- | "cloudflare"
9
- | "dashboard";
4
+ export type CreateMreactAppTemplate = "basic" | "tailwind" | "dashboard";
10
5
 
11
6
  export type CreateMreactAppPackageManager = "pnpm" | "npm" | "bun";
12
- export type CreateMreactAppDeployTarget = "aws-lambda" | "container";
7
+ export type CreateMreactAppDeployTarget = "aws-lambda" | "cloudflare" | "container";
13
8
 
14
9
  export interface CreateMreactAppOptions {
15
10
  deploy?: CreateMreactAppDeployTarget | undefined;
@@ -65,14 +60,14 @@ interface TemplateDefinition {
65
60
  }
66
61
 
67
62
  const internalPackageVersions = {
68
- "@reckona/mreact-auth": "^0.0.94",
69
- "@reckona/mreact-devtools": "^0.0.94",
70
- "@reckona/mreact-forms": "^0.0.94",
71
- "@reckona/mreact": "^0.0.94",
72
- "@reckona/mreact-query": "^0.0.94",
73
- "@reckona/mreact-reactive-core": "^0.0.94",
63
+ "@reckona/mreact-auth": "^0.0.95",
64
+ "@reckona/mreact-devtools": "^0.0.95",
65
+ "@reckona/mreact-forms": "^0.0.95",
66
+ "@reckona/mreact": "^0.0.95",
67
+ "@reckona/mreact-query": "^0.0.95",
68
+ "@reckona/mreact-reactive-core": "^0.0.95",
74
69
  "@reckona/mreact-reactive-dom": "^0.0.51",
75
- "@reckona/mreact-router": "^0.0.94",
70
+ "@reckona/mreact-router": "^0.0.95",
76
71
  "@reckona/mreact-test-utils": "^0.0.51",
77
72
  } as const satisfies Record<string, string>;
78
73
  const currentMreactVersion = internalPackageVersions["@reckona/mreact"].replace(/^\^/, "");
@@ -93,7 +88,7 @@ const pnpmOnlyBuiltDependencies = ["@parcel/watcher", "esbuild", "sharp", "worke
93
88
  export async function createMreactApp(
94
89
  options: CreateMreactAppOptions,
95
90
  ): Promise<CreateMreactAppResult> {
96
- const template = options.template ?? "app-router";
91
+ const template = options.template ?? "basic";
97
92
  const packageManager = options.packageManager ?? "pnpm";
98
93
  const name = await inferPackageNameForTarget(options.directory, options.name);
99
94
  const workspacePackages = await detectWorkspacePackagesForTarget(options.directory);
@@ -186,12 +181,16 @@ export async function upgradeMreactApp(
186
181
 
187
182
  export const createMreactAppTemplates = [
188
183
  "basic",
189
- "app-router",
190
- "app-router-tailwind",
191
- "cloudflare",
184
+ "tailwind",
192
185
  "dashboard",
193
186
  ] as const satisfies readonly CreateMreactAppTemplate[];
194
187
 
188
+ export const createMreactAppDeployTargets = [
189
+ "cloudflare",
190
+ "container",
191
+ "aws-lambda",
192
+ ] as const satisfies readonly CreateMreactAppDeployTarget[];
193
+
195
194
  export const createMreactAppCodemods = [
196
195
  {
197
196
  description:
@@ -228,19 +227,12 @@ function templateDefinition(
228
227
  deploy: CreateMreactAppDeployTarget | undefined,
229
228
  workspacePackages: ReadonlySet<string>,
230
229
  ): TemplateDefinition {
231
- if (template === "basic" || template === "app-router") {
232
- return appRouterTemplate(name, packageManager, workspacePackages, {
233
- cloudflare: false,
234
- dashboard: false,
235
- deploy,
236
- srcDir,
237
- tailwind: false,
238
- });
239
- }
230
+ // `cloudflare` is a deploy target, orthogonal to the app-content template.
231
+ const cloudflare = deploy === "cloudflare";
240
232
 
241
- if (template === "app-router-tailwind") {
233
+ if (template === "tailwind") {
242
234
  return appRouterTemplate(name, packageManager, workspacePackages, {
243
- cloudflare: false,
235
+ cloudflare,
244
236
  dashboard: false,
245
237
  deploy,
246
238
  srcDir,
@@ -250,7 +242,7 @@ function templateDefinition(
250
242
 
251
243
  if (template === "dashboard") {
252
244
  return appRouterTemplate(name, packageManager, workspacePackages, {
253
- cloudflare: false,
245
+ cloudflare,
254
246
  dashboard: true,
255
247
  deploy,
256
248
  srcDir,
@@ -259,7 +251,7 @@ function templateDefinition(
259
251
  }
260
252
 
261
253
  return appRouterTemplate(name, packageManager, workspacePackages, {
262
- cloudflare: true,
254
+ cloudflare,
263
255
  dashboard: false,
264
256
  deploy,
265
257
  srcDir,
@@ -570,10 +562,13 @@ function packageScripts(
570
562
  }
571
563
 
572
564
  if (options.cloudflare) {
565
+ const cloudflareBuild = "mreact-router build --target=cloudflare";
573
566
  scripts.deploy = "wrangler deploy";
574
567
  scripts.dev = `${run} build && wrangler dev`;
575
568
  scripts.preview = `${run} build && wrangler dev`;
576
- scripts.build = "mreact-router build --target=cloudflare";
569
+ scripts.build = options.tailwind
570
+ ? `${run} prepare:css && ${run} build:css && ${cloudflareBuild}`
571
+ : cloudflareBuild;
577
572
  }
578
573
 
579
574
  if (options.deploy === "aws-lambda") {