create-gtkx 1.0.0 → 1.1.0

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
@@ -161,7 +161,7 @@ Explore the [example apps](https://github.com/gtkx-org/gtkx/tree/main/examples):
161
161
 
162
162
  ## Status
163
163
 
164
- GTKX 1.0 is released.
164
+ GTKX is stable and ready for production use.
165
165
 
166
166
  ## Contributing
167
167
 
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
- import { packageVersion } from "@gtkx/utils";
2
1
  import { defineCommand, runMain } from "citty";
2
+ import packageManifest from "../package.json" with { type: "json" };
3
3
  import { scaffoldCommand } from "./command.js";
4
- const version = packageVersion(import.meta.url, "../package.json");
4
+ const version = packageManifest.version;
5
5
  const main = defineCommand({
6
6
  ...scaffoldCommand,
7
7
  meta: {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;AAEnE,MAAM,IAAI,GAAG,aAAa,CAAC;IACvB,GAAG,eAAe;IAClB,IAAI,EAAE;QACF,IAAI,EAAE,aAAa;QACnB,OAAO;QACP,WAAW,EAAE,iCAAiC;KACjD;CACJ,CAAC,CAAC;AAEH,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC","sourcesContent":["import { packageVersion } from \"@gtkx/utils\";\nimport { defineCommand, runMain } from \"citty\";\nimport { scaffoldCommand } from \"./command.js\";\n\nconst version = packageVersion(import.meta.url, \"../package.json\");\n\nconst main = defineCommand({\n ...scaffoldCommand,\n meta: {\n name: \"create-gtkx\",\n version,\n description: \"Scaffold a new GTKX application\",\n },\n});\n\nvoid runMain(main);\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,eAAe,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AAExC,MAAM,IAAI,GAAG,aAAa,CAAC;IACvB,GAAG,eAAe;IAClB,IAAI,EAAE;QACF,IAAI,EAAE,aAAa;QACnB,OAAO;QACP,WAAW,EAAE,iCAAiC;KACjD;CACJ,CAAC,CAAC;AAEH,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC","sourcesContent":["import { defineCommand, runMain } from \"citty\";\nimport packageManifest from \"../package.json\" with { type: \"json\" };\nimport { scaffoldCommand } from \"./command.js\";\n\nconst version = packageManifest.version;\n\nconst main = defineCommand({\n ...scaffoldCommand,\n meta: {\n name: \"create-gtkx\",\n version,\n description: \"Scaffold a new GTKX application\",\n },\n});\n\nvoid runMain(main);\n"]}
package/dist/command.d.ts CHANGED
@@ -1,13 +1,3 @@
1
- type CreateCommandArgs = {
2
- name?: string | undefined;
3
- "application-id"?: string | undefined;
4
- "package-manager"?: string | undefined;
5
- typescript?: boolean | undefined;
6
- vitest?: boolean | undefined;
7
- yes?: boolean | undefined;
8
- "no-interactive"?: boolean | undefined;
9
- overwrite?: boolean | undefined;
10
- };
11
1
  declare const scaffoldCommand: import("citty").CommandDef<{
12
2
  readonly name: {
13
3
  readonly type: "positional";
@@ -20,7 +10,7 @@ declare const scaffoldCommand: import("citty").CommandDef<{
20
10
  };
21
11
  readonly "package-manager": {
22
12
  readonly type: "string";
23
- readonly alias: "pm";
13
+ readonly alias: "p";
24
14
  readonly description: string;
25
15
  };
26
16
  readonly typescript: {
@@ -43,10 +33,9 @@ declare const scaffoldCommand: import("citty").CommandDef<{
43
33
  };
44
34
  readonly overwrite: {
45
35
  readonly type: "boolean";
46
- readonly alias: "force";
36
+ readonly alias: "f";
47
37
  readonly description: "Overwrite the contents of a non-empty target directory when running without prompts";
48
38
  };
49
39
  }>;
50
- declare const runCreate: (args: CreateCommandArgs) => Promise<void>;
51
- export { scaffoldCommand, runCreate };
40
+ export { scaffoldCommand };
52
41
  //# sourceMappingURL=command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAKA,KAAK,iBAAiB,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnC,CAAC;AAEF,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CnB,CAAC;AA4BH,QAAA,MAAM,SAAS,GAAU,MAAM,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAiB9D,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CnB,CAAC;AAgCH,OAAO,EAAE,eAAe,EAAE,CAAC"}
package/dist/command.js CHANGED
@@ -1,6 +1,8 @@
1
+ import * as p from "@clack/prompts";
2
+ import { errorMessage } from "@gtkx/utils";
1
3
  import { defineCommand } from "citty";
2
4
  import { OperationCanceledError, ScaffoldAbortedError } from "./errors.js";
3
- import { isKnownPackageManager, PACKAGE_MANAGER_FLAG_DESCRIPTION } from "./package-managers.js";
5
+ import { PACKAGE_MANAGER_FLAG_DESCRIPTION } from "./package-managers.js";
4
6
  import { scaffold } from "./scaffolder.js";
5
7
  const scaffoldCommand = defineCommand({
6
8
  meta: {
@@ -19,7 +21,7 @@ const scaffoldCommand = defineCommand({
19
21
  },
20
22
  "package-manager": {
21
23
  type: "string",
22
- alias: "pm",
24
+ alias: "p",
23
25
  description: PACKAGE_MANAGER_FLAG_DESCRIPTION,
24
26
  },
25
27
  typescript: {
@@ -43,39 +45,28 @@ const scaffoldCommand = defineCommand({
43
45
  },
44
46
  overwrite: {
45
47
  type: "boolean",
46
- alias: "force",
48
+ alias: "f",
47
49
  description: "Overwrite the contents of a non-empty target directory when running without prompts",
48
50
  },
49
51
  },
50
52
  run: ({ args }) => runCreate(args),
51
53
  });
52
- const parsePackageManager = (value) => {
53
- if (value === undefined) {
54
- return undefined;
55
- }
56
- if (!isKnownPackageManager(value)) {
57
- throw new Error(`Unknown package manager "${value}". Expected one of: ${PACKAGE_MANAGER_FLAG_DESCRIPTION}.`);
58
- }
59
- return value;
60
- };
61
54
  const settleScaffoldFailure = (error) => {
62
55
  if (error instanceof OperationCanceledError) {
63
56
  return;
64
57
  }
65
- if (error instanceof ScaffoldAbortedError) {
66
- process.exitCode = 1;
67
- return;
58
+ if (!(error instanceof ScaffoldAbortedError)) {
59
+ p.log.error(errorMessage(error));
68
60
  }
69
- throw error;
61
+ process.exitCode = 1;
70
62
  };
71
63
  const runCreate = async (args) => {
72
64
  const isInteractive = args["no-interactive"] || args.yes ? false : process.stdin.isTTY;
73
- const packageManager = parsePackageManager(args["package-manager"]);
74
65
  try {
75
66
  await scaffold({
76
67
  name: args.name,
77
68
  applicationId: args["application-id"],
78
- packageManager,
69
+ packageManager: args["package-manager"],
79
70
  isTypescript: args.typescript,
80
71
  shouldIncludeTesting: args.vitest,
81
72
  isInteractive,
@@ -86,5 +77,5 @@ const runCreate = async (args) => {
86
77
  settleScaffoldFailure(error);
87
78
  }
88
79
  };
89
- export { scaffoldCommand, runCreate };
80
+ export { scaffoldCommand };
90
81
  //# sourceMappingURL=command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineCommand } from \"citty\";\nimport { OperationCanceledError, ScaffoldAbortedError } from \"./errors.js\";\nimport { isKnownPackageManager, PACKAGE_MANAGER_FLAG_DESCRIPTION, type PackageManager } from \"./package-managers.js\";\nimport { scaffold } from \"./scaffolder.js\";\n\ntype CreateCommandArgs = {\n name?: string | undefined;\n \"application-id\"?: string | undefined;\n \"package-manager\"?: string | undefined;\n typescript?: boolean | undefined;\n vitest?: boolean | undefined;\n yes?: boolean | undefined;\n \"no-interactive\"?: boolean | undefined;\n overwrite?: boolean | undefined;\n};\n\nconst scaffoldCommand = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a new GTKX application\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Target directory or project name (e.g. my-app, ., apps/my-app)\",\n required: false,\n },\n \"application-id\": {\n type: \"string\",\n description: \"Application ID (e.g., com.example.myapp)\",\n },\n \"package-manager\": {\n type: \"string\",\n alias: \"pm\",\n description: PACKAGE_MANAGER_FLAG_DESCRIPTION,\n },\n typescript: {\n type: \"boolean\",\n negativeDescription: \"Scaffold the application in JavaScript instead of TypeScript\",\n description: \"Scaffold the application in TypeScript\",\n },\n vitest: {\n type: \"boolean\",\n description: \"Include a Vitest testing setup\",\n },\n yes: {\n type: \"boolean\",\n alias: \"y\",\n description: \"Skip prompts and accept defaults for unspecified options\",\n },\n \"no-interactive\": {\n type: \"boolean\",\n description:\n \"Run without prompts, using the default for every option not passed on the command line \" +\n \"(same as --yes)\",\n },\n overwrite: {\n type: \"boolean\",\n alias: \"force\",\n description: \"Overwrite the contents of a non-empty target directory when running without prompts\",\n },\n },\n run: ({ args }) => runCreate(args),\n});\n\nconst parsePackageManager = (value: string | undefined): PackageManager | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isKnownPackageManager(value)) {\n throw new Error(`Unknown package manager \"${value}\". Expected one of: ${PACKAGE_MANAGER_FLAG_DESCRIPTION}.`);\n }\n\n return value;\n};\n\nconst settleScaffoldFailure = (error: unknown): void => {\n if (error instanceof OperationCanceledError) {\n return;\n }\n\n if (error instanceof ScaffoldAbortedError) {\n process.exitCode = 1;\n\n return;\n }\n\n throw error;\n};\n\nconst runCreate = async (args: CreateCommandArgs): Promise<void> => {\n const isInteractive = args[\"no-interactive\"] || args.yes ? false : process.stdin.isTTY;\n const packageManager = parsePackageManager(args[\"package-manager\"]);\n\n try {\n await scaffold({\n name: args.name,\n applicationId: args[\"application-id\"],\n packageManager,\n isTypescript: args.typescript,\n shouldIncludeTesting: args.vitest,\n isInteractive,\n shouldOverwrite: args.overwrite,\n });\n } catch (error) {\n settleScaffoldFailure(error);\n }\n};\n\nexport { scaffoldCommand, runCreate };\n"]}
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAa3C,MAAM,eAAe,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE;QACF,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,+BAA+B;KAC/C;IACD,IAAI,EAAE;QACF,IAAI,EAAE;YACF,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,gEAAgE;YAC7E,QAAQ,EAAE,KAAK;SAClB;QACD,gBAAgB,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,0CAA0C;SAC1D;QACD,iBAAiB,EAAE;YACf,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,WAAW,EAAE,gCAAgC;SAChD;QACD,UAAU,EAAE;YACR,IAAI,EAAE,SAAS;YACf,mBAAmB,EAAE,8DAA8D;YACnF,WAAW,EAAE,wCAAwC;SACxD;QACD,MAAM,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,gCAAgC;SAChD;QACD,GAAG,EAAE;YACD,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,GAAG;YACV,WAAW,EAAE,0DAA0D;SAC1E;QACD,gBAAgB,EAAE;YACd,IAAI,EAAE,SAAS;YACf,WAAW,EACP,yFAAyF;gBACzF,iBAAiB;SACxB;QACD,SAAS,EAAE;YACP,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,GAAG;YACV,WAAW,EAAE,qFAAqF;SACrG;KACJ;IACD,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAQ,EAAE;IACnD,IAAI,KAAK,YAAY,sBAAsB,EAAE,CAAC;QAC1C,OAAO;IACX,CAAC;IAED,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAoB,CAAC,EAAE,CAAC;QAC3C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACzB,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,KAAK,EAAE,IAAuB,EAAiB,EAAE;IAC/D,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAEvF,IAAI,CAAC;QACD,MAAM,QAAQ,CAAC;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,aAAa,EAAE,IAAI,CAAC,gBAAgB,CAAC;YACrC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC;YACvC,YAAY,EAAE,IAAI,CAAC,UAAU;YAC7B,oBAAoB,EAAE,IAAI,CAAC,MAAM;YACjC,aAAa;YACb,eAAe,EAAE,IAAI,CAAC,SAAS;SAClC,CAAC,CAAC;IACP,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;AACL,CAAC,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import * as p from \"@clack/prompts\";\nimport { errorMessage } from \"@gtkx/utils\";\nimport { defineCommand } from \"citty\";\nimport { OperationCanceledError, ScaffoldAbortedError } from \"./errors.js\";\nimport { PACKAGE_MANAGER_FLAG_DESCRIPTION } from \"./package-managers.js\";\nimport { scaffold } from \"./scaffolder.js\";\n\ntype CreateCommandArgs = {\n name?: string | undefined;\n \"application-id\"?: string | undefined;\n \"package-manager\"?: string | undefined;\n typescript?: boolean | undefined;\n vitest?: boolean | undefined;\n yes?: boolean | undefined;\n \"no-interactive\"?: boolean | undefined;\n overwrite?: boolean | undefined;\n};\n\nconst scaffoldCommand = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a new GTKX application\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Target directory or project name (e.g. my-app, ., apps/my-app)\",\n required: false,\n },\n \"application-id\": {\n type: \"string\",\n description: \"Application ID (e.g., com.example.myapp)\",\n },\n \"package-manager\": {\n type: \"string\",\n alias: \"p\",\n description: PACKAGE_MANAGER_FLAG_DESCRIPTION,\n },\n typescript: {\n type: \"boolean\",\n negativeDescription: \"Scaffold the application in JavaScript instead of TypeScript\",\n description: \"Scaffold the application in TypeScript\",\n },\n vitest: {\n type: \"boolean\",\n description: \"Include a Vitest testing setup\",\n },\n yes: {\n type: \"boolean\",\n alias: \"y\",\n description: \"Skip prompts and accept defaults for unspecified options\",\n },\n \"no-interactive\": {\n type: \"boolean\",\n description:\n \"Run without prompts, using the default for every option not passed on the command line \" +\n \"(same as --yes)\",\n },\n overwrite: {\n type: \"boolean\",\n alias: \"f\",\n description: \"Overwrite the contents of a non-empty target directory when running without prompts\",\n },\n },\n run: ({ args }) => runCreate(args),\n});\n\nconst settleScaffoldFailure = (error: unknown): void => {\n if (error instanceof OperationCanceledError) {\n return;\n }\n\n if (!(error instanceof ScaffoldAbortedError)) {\n p.log.error(errorMessage(error));\n }\n\n process.exitCode = 1;\n};\n\nconst runCreate = async (args: CreateCommandArgs): Promise<void> => {\n const isInteractive = args[\"no-interactive\"] || args.yes ? false : process.stdin.isTTY;\n\n try {\n await scaffold({\n name: args.name,\n applicationId: args[\"application-id\"],\n packageManager: args[\"package-manager\"],\n isTypescript: args.typescript,\n shouldIncludeTesting: args.vitest,\n isInteractive,\n shouldOverwrite: args.overwrite,\n });\n } catch (error) {\n settleScaffoldFailure(error);\n }\n};\n\nexport { scaffoldCommand };\n"]}
@@ -18,7 +18,8 @@ declare const PACKAGE_MANAGERS: readonly [{
18
18
  readonly addCommand: "yarn add";
19
19
  readonly isRecommended: false;
20
20
  }];
21
+ declare const PACKAGE_MANAGER_VALUES: PackageManager[];
21
22
  declare const PACKAGE_MANAGER_FLAG_DESCRIPTION: string;
22
23
  declare const isKnownPackageManager: (name: string) => name is PackageManager;
23
- export { PACKAGE_MANAGERS, PACKAGE_MANAGER_FLAG_DESCRIPTION, isKnownPackageManager, type PackageManager };
24
+ export { PACKAGE_MANAGERS, PACKAGE_MANAGER_FLAG_DESCRIPTION, PACKAGE_MANAGER_VALUES, isKnownPackageManager, type PackageManager, };
24
25
  //# sourceMappingURL=package-managers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-managers.d.ts","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;AAEjE,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAIZ,CAAC;AAGX,QAAA,MAAM,gCAAgC,QAA2D,CAAC;AAElG,QAAA,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,IAAI,IAAI,cACC,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,gCAAgC,EAAE,qBAAqB,EAAE,KAAK,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"package-managers.d.ts","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;AAEjE,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAIZ,CAAC;AAEX,QAAA,MAAM,sBAAsB,EAAE,cAAc,EAAqD,CAAC;AAClG,QAAA,MAAM,gCAAgC,QAA2D,CAAC;AAElG,QAAA,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,IAAI,IAAI,cACC,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,gCAAgC,EAChC,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,cAAc,GACtB,CAAC"}
@@ -6,5 +6,5 @@ const PACKAGE_MANAGERS = [
6
6
  const PACKAGE_MANAGER_VALUES = PACKAGE_MANAGERS.map((manager) => manager.value);
7
7
  const PACKAGE_MANAGER_FLAG_DESCRIPTION = `Package manager (${PACKAGE_MANAGER_VALUES.join(", ")})`;
8
8
  const isKnownPackageManager = (name) => PACKAGE_MANAGER_VALUES.includes(name);
9
- export { PACKAGE_MANAGERS, PACKAGE_MANAGER_FLAG_DESCRIPTION, isKnownPackageManager };
9
+ export { PACKAGE_MANAGERS, PACKAGE_MANAGER_FLAG_DESCRIPTION, PACKAGE_MANAGER_VALUES, isKnownPackageManager, };
10
10
  //# sourceMappingURL=package-managers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-managers.js","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG;IACrB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE;IACrG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE;IAC1G,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE;CAChG,CAAC;AAEX,MAAM,sBAAsB,GAAqB,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAClG,MAAM,gCAAgC,GAAG,oBAAoB,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAElG,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAA0B,EAAE,CAClE,sBAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,gCAAgC,EAAE,qBAAqB,EAAuB,CAAC","sourcesContent":["type PackageManager = (typeof PACKAGE_MANAGERS)[number][\"value\"];\n\nconst PACKAGE_MANAGERS = [\n { value: \"pnpm\", label: \"pnpm\", devCommand: \"pnpm dev\", addCommand: \"pnpm add\", isRecommended: true },\n { value: \"npm\", label: \"npm\", devCommand: \"npm run dev\", addCommand: \"npm install\", isRecommended: false },\n { value: \"yarn\", label: \"yarn\", devCommand: \"yarn dev\", addCommand: \"yarn add\", isRecommended: false },\n] as const;\n\nconst PACKAGE_MANAGER_VALUES: PackageManager[] = PACKAGE_MANAGERS.map((manager) => manager.value);\nconst PACKAGE_MANAGER_FLAG_DESCRIPTION = `Package manager (${PACKAGE_MANAGER_VALUES.join(\", \")})`;\n\nconst isKnownPackageManager = (name: string): name is PackageManager =>\n (PACKAGE_MANAGER_VALUES as string[]).includes(name);\n\nexport { PACKAGE_MANAGERS, PACKAGE_MANAGER_FLAG_DESCRIPTION, isKnownPackageManager, type PackageManager };\n"]}
1
+ {"version":3,"file":"package-managers.js","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG;IACrB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE;IACrG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE;IAC1G,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE;CAChG,CAAC;AAEX,MAAM,sBAAsB,GAAqB,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAClG,MAAM,gCAAgC,GAAG,oBAAoB,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAElG,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAA0B,EAAE,CAClE,sBAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,gCAAgC,EAChC,sBAAsB,EACtB,qBAAqB,GAExB,CAAC","sourcesContent":["type PackageManager = (typeof PACKAGE_MANAGERS)[number][\"value\"];\n\nconst PACKAGE_MANAGERS = [\n { value: \"pnpm\", label: \"pnpm\", devCommand: \"pnpm dev\", addCommand: \"pnpm add\", isRecommended: true },\n { value: \"npm\", label: \"npm\", devCommand: \"npm run dev\", addCommand: \"npm install\", isRecommended: false },\n { value: \"yarn\", label: \"yarn\", devCommand: \"yarn dev\", addCommand: \"yarn add\", isRecommended: false },\n] as const;\n\nconst PACKAGE_MANAGER_VALUES: PackageManager[] = PACKAGE_MANAGERS.map((manager) => manager.value);\nconst PACKAGE_MANAGER_FLAG_DESCRIPTION = `Package manager (${PACKAGE_MANAGER_VALUES.join(\", \")})`;\n\nconst isKnownPackageManager = (name: string): name is PackageManager =>\n (PACKAGE_MANAGER_VALUES as string[]).includes(name);\n\nexport {\n PACKAGE_MANAGERS,\n PACKAGE_MANAGER_FLAG_DESCRIPTION,\n PACKAGE_MANAGER_VALUES,\n isKnownPackageManager,\n type PackageManager,\n};\n"]}
@@ -1,13 +1,12 @@
1
- import { type PackageManager } from "./package-managers.js";
2
1
  type CreateOptions = {
3
2
  name?: string | undefined;
4
3
  applicationId?: string | undefined;
5
- packageManager?: PackageManager | undefined;
4
+ packageManager?: string | undefined;
6
5
  isTypescript?: boolean | undefined;
7
6
  shouldIncludeTesting?: boolean | undefined;
8
7
  isInteractive?: boolean | undefined;
9
8
  shouldOverwrite?: boolean | undefined;
10
9
  };
11
10
  declare const scaffold: (options?: CreateOptions) => Promise<void>;
12
- export { scaffold, type CreateOptions };
11
+ export { scaffold };
13
12
  //# sourceMappingURL=scaffolder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffolder.d.ts","sourceRoot":"","sources":["../src/scaffolder.ts"],"names":[],"mappings":"AAWA,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIrG,KAAK,aAAa,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACzC,CAAC;AAwfF,QAAA,MAAM,QAAQ,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAwBjE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"scaffolder.d.ts","sourceRoot":"","sources":["../src/scaffolder.ts"],"names":[],"mappings":"AAsBA,KAAK,aAAa,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACzC,CAAC;AAumBF,QAAA,MAAM,QAAQ,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAcjE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1,18 +1,21 @@
1
1
  import * as p from "@clack/prompts";
2
- import { isValidApplicationId } from "@gtkx/config/internal";
3
- import { errorMessage, packageVersion, upperFirst } from "@gtkx/utils";
4
- import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { APPLICATION_ID_MAX_LENGTH, isValidApplicationId } from "@gtkx/config/internal";
3
+ import { errorMessage, tryResolveExecutable, upperFirst } from "@gtkx/utils";
4
+ import { execFileSync } from "node:child_process";
5
+ import { mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
5
6
  import { basename, dirname, join, resolve } from "node:path";
6
7
  import { addDependency, detectPackageManager as nypmDetectPackageManager } from "nypm";
7
8
  import { x } from "tinyexec";
9
+ import packageManifest from "../package.json" with { type: "json" };
8
10
  import { writeBuildAllowance } from "./build-allowance.js";
9
11
  import { OperationCanceledError, ScaffoldAbortedError } from "./errors.js";
10
12
  import { getInstallHint } from "./install-hints.js";
11
13
  import { updateManifest } from "./manifest.js";
12
- import { isKnownPackageManager, PACKAGE_MANAGERS } from "./package-managers.js";
14
+ import { isKnownPackageManager, PACKAGE_MANAGER_VALUES, PACKAGE_MANAGERS, } from "./package-managers.js";
13
15
  import { isValidProjectName } from "./project-name.js";
14
16
  import { listTemplates, renderFile } from "./templates.js";
15
- const selfVersion = packageVersion(import.meta.url, "../package.json");
17
+ const selfVersion = packageManifest.version;
18
+ const ICON_TEMPLATE_NAME = "icon.svg";
16
19
  const GTKX_ENV_MODULE_HEADER = `/**
17
20
  * Generated by \`gtkx codegen\`, \`gtkx dev\`, and \`gtkx build\`; do not edit.
18
21
  */`;
@@ -31,11 +34,33 @@ To run tests, you need a headless Wayland compositor installed:
31
34
  Fedora: sudo dnf install sway
32
35
  Ubuntu: sudo apt install sway`;
33
36
  const RECOVERY_HEADING = "Finish the setup by adding the dependencies again, which is safe to repeat:";
37
+ const APPLICATION_ID_FORMAT_ERROR = "Application ID must be reverse domain notation (e.g., com.example.myapp)";
38
+ const APPLICATION_ID_PREFIX = "com.";
39
+ const APPLICATION_ID_SUFFIX = ".app";
40
+ const APPLICATION_ID_SEGMENT_LIMIT = APPLICATION_ID_MAX_LENGTH - APPLICATION_ID_PREFIX.length -
41
+ APPLICATION_ID_SUFFIX.length;
34
42
  const pinGtkxDependency = (name, version) => name.startsWith("@gtkx/") ? `${name}@^${version}` : name;
35
43
  const getDevCommand = (packageManager) => DEV_COMMAND[packageManager];
36
44
  const getAddCommand = (packageManager) => ADD_COMMAND[packageManager];
37
45
  const titleFromName = (name) => name.split("-").map((part) => upperFirst(part)).join(" ");
38
- const suggestApplicationId = (name) => `com.${name.replaceAll("-", "")}.app`;
46
+ const gitConfigValue = (key) => {
47
+ const git = tryResolveExecutable("git");
48
+ if (git === undefined) {
49
+ return null;
50
+ }
51
+ try {
52
+ return execFileSync(git, ["config", "--get", key], { encoding: "utf8" }).trim() || null;
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ };
58
+ const applicationIdSegment = (name) => {
59
+ const compact = name.replaceAll("-", "");
60
+ const segment = /^[A-Za-z_]/.test(compact) ? compact : `_${compact}`;
61
+ return segment.slice(0, APPLICATION_ID_SEGMENT_LIMIT);
62
+ };
63
+ const suggestApplicationId = (name) => `${APPLICATION_ID_PREFIX}${applicationIdSegment(name)}${APPLICATION_ID_SUFFIX}`;
39
64
  const stripTrailingSlashes = (value) => {
40
65
  let end = value.length;
41
66
  while (end > 0 && value[end - 1] === "/") {
@@ -75,37 +100,49 @@ const validateProjectName = (value) => {
75
100
  }
76
101
  return undefined;
77
102
  };
78
- const validateApplicationIdInput = (value) => {
79
- if (!value) {
80
- return "Application ID is required";
81
- }
82
- if (!isValidApplicationId(value)) {
83
- return "Application ID must be reverse domain notation (e.g., com.example.myapp)";
84
- }
85
- return undefined;
86
- };
103
+ const validateApplicationIdFormat = (value) => isValidApplicationId(value) ? undefined : APPLICATION_ID_FORMAT_ERROR;
104
+ const validateApplicationIdInput = (value) => value ? validateApplicationIdFormat(value) : "Application ID is required";
105
+ const validateApplicationIdAnswer = (value) => value ? validateApplicationIdFormat(value) : undefined;
87
106
  const fail = (message) => {
88
107
  p.log.error(message);
89
108
  throw new ScaffoldAbortedError(message);
90
109
  };
91
- const validateTarget = (value) => {
92
- if (!value) {
110
+ const requestedPackageManager = (value) => {
111
+ if (value === undefined) {
112
+ return undefined;
113
+ }
114
+ if (!isKnownPackageManager(value)) {
115
+ return fail(`Unknown package manager "${value}". Expected one of: ${PACKAGE_MANAGER_VALUES.join(", ")}`);
116
+ }
117
+ return value;
118
+ };
119
+ const targetStats = (root) => {
120
+ try {
121
+ return statSync(root, { throwIfNoEntry: false });
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ };
127
+ const validateTargetDir = (target) => {
128
+ if (!target) {
93
129
  return "Project directory is required";
94
130
  }
95
- return validateProjectName(deriveProjectName(formatTargetDir(value)));
131
+ return validateProjectName(deriveProjectName(target));
96
132
  };
97
- const promptTarget = async () => guardCancellation(await p.text({
133
+ const validateTargetAnswer = (value) => validateTargetDir(formatTargetDir(value ?? ""));
134
+ const promptTarget = async () => formatTargetDir(guardCancellation(await p.text({
98
135
  message: "Project directory",
99
136
  placeholder: "my-app",
100
- validate: validateTarget,
101
- }));
137
+ validate: validateTargetAnswer,
138
+ })));
102
139
  const promptApplicationId = async (name) => {
103
140
  const defaultApplicationId = suggestApplicationId(name);
104
141
  return guardCancellation(await p.text({
105
142
  message: "Application ID",
106
143
  placeholder: defaultApplicationId,
107
- initialValue: defaultApplicationId,
108
- validate: validateApplicationIdInput,
144
+ defaultValue: defaultApplicationId,
145
+ validate: validateApplicationIdAnswer,
109
146
  }));
110
147
  };
111
148
  const detectPackageManager = async (cwd) => {
@@ -161,14 +198,10 @@ const emptyDir = (root) => {
161
198
  rmSync(join(root, entry), { recursive: true, force: true });
162
199
  }
163
200
  };
164
- const validateResolvedOptions = (name, applicationId) => {
165
- const nameError = validateProjectName(name);
166
- if (nameError) {
167
- fail(nameError);
168
- }
169
- const applicationIdError = validateApplicationIdInput(applicationId);
170
- if (applicationIdError) {
171
- fail(applicationIdError);
201
+ const prepareTargetDirectory = (root, shouldEmptyTarget) => {
202
+ mkdirSync(root, { recursive: true });
203
+ if (shouldEmptyTarget) {
204
+ emptyDir(root);
172
205
  }
173
206
  };
174
207
  const shouldOverwriteDirectory = async (target, options) => {
@@ -180,34 +213,39 @@ const shouldOverwriteDirectory = async (target, options) => {
180
213
  initialValue: false,
181
214
  }));
182
215
  };
183
- const handleTargetDirectory = async (root, target, options) => {
184
- if (!existsSync(root) || isDirEmpty(root)) {
185
- return;
216
+ const shouldEmptyTargetDirectory = async (root, target, options) => {
217
+ const stats = targetStats(root);
218
+ if (stats === undefined) {
219
+ return false;
220
+ }
221
+ if (!stats.isDirectory()) {
222
+ return fail(`Target "${target}" is not a directory`);
223
+ }
224
+ if (isDirEmpty(root)) {
225
+ return false;
186
226
  }
187
227
  if (await shouldOverwriteDirectory(target, options)) {
188
- emptyDir(root);
189
- return;
228
+ return true;
190
229
  }
191
- fail(`Directory "${target}" is not empty`);
230
+ return fail(`Directory "${target}" is not empty`);
192
231
  };
193
232
  const resolveTarget = async (options) => {
194
- if (options.name !== undefined) {
195
- return formatTargetDir(options.name);
233
+ if (options.name === undefined && options.isInteractive) {
234
+ return promptTarget();
196
235
  }
197
- if (!options.isInteractive) {
198
- return fail("Project directory is required");
199
- }
200
- return formatTargetDir(await promptTarget());
236
+ const target = formatTargetDir(options.name ?? "");
237
+ const error = validateTargetDir(target);
238
+ return error === undefined ? target : fail(error);
201
239
  };
202
240
  const resolveApplicationId = async (options, name) => {
203
- if (options.applicationId !== undefined) {
204
- return options.applicationId;
205
- }
206
- return options.isInteractive ? promptApplicationId(name) : suggestApplicationId(name);
241
+ const applicationId = options.applicationId ??
242
+ (options.isInteractive ? await promptApplicationId(name) : suggestApplicationId(name));
243
+ const error = validateApplicationIdInput(applicationId);
244
+ return error === undefined ? applicationId : fail(error);
207
245
  };
208
- const resolvePackageManager = async (options) => {
209
- if (options.packageManager !== undefined) {
210
- return options.packageManager;
246
+ const resolvePackageManager = async (requested, options) => {
247
+ if (requested !== undefined) {
248
+ return requested;
211
249
  }
212
250
  if (options.isInteractive) {
213
251
  return promptPackageManager();
@@ -227,16 +265,27 @@ const isTestingIncluded = async (options) => {
227
265
  return options.isInteractive ? shouldSetUpTesting() : true;
228
266
  };
229
267
  const resolveOptions = async (options) => {
268
+ const requested = requestedPackageManager(options.packageManager);
230
269
  const target = await resolveTarget(options);
231
270
  const name = deriveProjectName(target);
232
271
  const applicationId = await resolveApplicationId(options, name);
233
- validateResolvedOptions(name, applicationId);
234
272
  const root = resolve(process.cwd(), target);
235
- await handleTargetDirectory(root, target, options);
236
- const packageManager = await resolvePackageManager(options);
273
+ const isCurrentDirectory = root === process.cwd();
274
+ const shouldEmptyTarget = await shouldEmptyTargetDirectory(root, target, options);
275
+ const packageManager = await resolvePackageManager(requested, options);
237
276
  const isTypescript = await isTypescriptSelected(options);
238
277
  const shouldIncludeTesting = await isTestingIncluded(options);
239
- return { target, name, applicationId, packageManager, isTypescript, shouldIncludeTesting };
278
+ return {
279
+ target,
280
+ root,
281
+ isCurrentDirectory,
282
+ name,
283
+ applicationId,
284
+ packageManager,
285
+ isTypescript,
286
+ shouldIncludeTesting,
287
+ shouldEmptyTarget,
288
+ };
240
289
  };
241
290
  const isTemplateIncluded = (templateRelativePath, resolved) => {
242
291
  if (TESTING_TEMPLATES.has(templateRelativePath)) {
@@ -247,28 +296,32 @@ const isTemplateIncluded = (templateRelativePath, resolved) => {
247
296
  }
248
297
  return true;
249
298
  };
250
- const toDestinationPath = (templateRelativePath, isTypescript) => isTypescript ? templateRelativePath : templateRelativePath.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".js");
299
+ const toScriptExtension = (templateRelativePath, isTypescript) => isTypescript ? templateRelativePath : templateRelativePath.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".js");
300
+ const toIconPath = (path, applicationId) => path.endsWith(ICON_TEMPLATE_NAME) ? `${path.slice(0, -ICON_TEMPLATE_NAME.length)}${applicationId}.svg` : path;
301
+ const toDestinationPath = (templateRelativePath, isTypescript, applicationId) => toIconPath(toScriptExtension(templateRelativePath, isTypescript), applicationId);
251
302
  const addTestScript = (root) => {
252
303
  updateManifest(root, (manifest) => {
253
304
  manifest.scripts.test = "vitest run";
254
305
  });
255
306
  };
307
+ const templateContext = (resolved) => ({
308
+ name: resolved.name,
309
+ applicationId: resolved.applicationId,
310
+ title: titleFromName(resolved.name),
311
+ shouldIncludeTesting: resolved.shouldIncludeTesting,
312
+ isTypescript: resolved.isTypescript,
313
+ importExtension: resolved.isTypescript ? ".js" : ".jsx",
314
+ developerName: gitConfigValue("user.name") ?? titleFromName(resolved.name),
315
+ developerEmail: gitConfigValue("user.email"),
316
+ });
256
317
  const scaffoldProject = async (root, resolved) => {
257
- const { name, applicationId, isTypescript, shouldIncludeTesting } = resolved;
258
- const context = {
259
- name,
260
- applicationId,
261
- title: titleFromName(name),
262
- shouldIncludeTesting,
263
- isTypescript,
264
- importExtension: isTypescript ? ".js" : ".jsx",
265
- };
266
- mkdirSync(root, { recursive: true });
318
+ const { applicationId, isTypescript, shouldIncludeTesting } = resolved;
319
+ const context = templateContext(resolved);
267
320
  for (const template of listTemplates()) {
268
321
  if (!isTemplateIncluded(template, resolved)) {
269
322
  continue;
270
323
  }
271
- const destination = join(root, toDestinationPath(template, isTypescript));
324
+ const destination = join(root, toDestinationPath(template, isTypescript, applicationId));
272
325
  mkdirSync(dirname(destination), { recursive: true });
273
326
  writeFileSync(destination, await renderFile(template, context));
274
327
  }
@@ -299,40 +352,53 @@ const installDependencies = async (options) => {
299
352
  });
300
353
  };
301
354
  const pin = (names) => names.map((dependency) => pinGtkxDependency(dependency, selfVersion));
302
- const formatRecovery = ({ target, packageManager, devDependencies }) => {
303
- const add = getAddCommand(packageManager);
355
+ const formatRecovery = ({ resolved, devDependencies }) => {
356
+ const add = getAddCommand(resolved.packageManager);
304
357
  const steps = [
305
- ...(target === "." ? [] : [`cd ${target}`]),
358
+ ...(resolved.isCurrentDirectory ? [] : [`cd ${resolved.target}`]),
306
359
  `${add} ${pin(DEPENDENCIES).join(" ")}`,
307
360
  `${add} -D ${pin(devDependencies).join(" ")}`,
308
361
  ];
309
362
  const indented = steps.map((step) => ` ${step}`).join("\n");
310
363
  return `${RECOVERY_HEADING}\n${indented}`;
311
364
  };
312
- const reportInstallFailure = (options) => {
313
- const message = errorMessage(options.error);
314
- p.log.error(`Failed to install dependencies: ${message}`);
315
- const hint = getInstallHint(message);
365
+ const explainInstallFailure = (options) => {
366
+ const hint = getInstallHint(errorMessage(options.error));
316
367
  if (hint !== undefined) {
317
368
  p.log.warn(hint);
318
369
  }
319
370
  p.log.info(formatRecovery(options));
320
371
  };
321
- const installAllDependencies = async (options) => {
322
- const { root, target, packageManager, devDependencies } = options;
372
+ const runWithSpinner = async (step) => {
323
373
  const spinner = p.spinner();
324
- spinner.start("Installing dependencies...");
374
+ spinner.start(step.pending);
325
375
  try {
326
- await installDependencies({ cwd: root, packageManager, dependencies: pin(DEPENDENCIES), isDev: false });
327
- await installDependencies({ cwd: root, packageManager, dependencies: pin(devDependencies), isDev: true });
328
- spinner.stop("Dependencies installed");
376
+ await step.run();
377
+ spinner.stop(step.done);
329
378
  }
330
379
  catch (error) {
331
- spinner.stop("Failed to install dependencies");
332
- reportInstallFailure({ error, target, packageManager, devDependencies });
333
- throw new ScaffoldAbortedError("Failed to install dependencies");
380
+ spinner.error(step.failed);
381
+ p.log.error(errorMessage(error));
382
+ step.explain?.(error);
383
+ throw new ScaffoldAbortedError(step.failed);
334
384
  }
335
385
  };
386
+ const installAllDependencies = async (options) => {
387
+ const { resolved, devDependencies } = options;
388
+ const { root, packageManager } = resolved;
389
+ await runWithSpinner({
390
+ pending: "Installing dependencies...",
391
+ done: "Dependencies installed",
392
+ failed: "Failed to install dependencies",
393
+ run: async () => {
394
+ await installDependencies({ cwd: root, packageManager, dependencies: pin(DEPENDENCIES), isDev: false });
395
+ await installDependencies({ cwd: root, packageManager, dependencies: pin(devDependencies), isDev: true });
396
+ },
397
+ explain: (error) => {
398
+ explainInstallFailure({ error, resolved, devDependencies });
399
+ },
400
+ });
401
+ };
336
402
  const writeInitialEnvModule = (root) => {
337
403
  const storeDir = join(root, "node_modules", ".gtkx");
338
404
  mkdirSync(storeDir, { recursive: true });
@@ -349,31 +415,34 @@ const initializeGitRepo = async (root) => {
349
415
  spinner.stop("Git repository initialized");
350
416
  }
351
417
  catch {
352
- spinner.stop("Failed to initialize git repository");
418
+ spinner.error("Failed to initialize git repository");
353
419
  }
354
420
  };
355
421
  const printNextSteps = (resolved) => {
356
422
  const devCmd = getDevCommand(resolved.packageManager);
357
- const cdStep = resolved.target === "." ? "" : `cd ${resolved.target}\n`;
423
+ const cdStep = resolved.isCurrentDirectory ? "" : `cd ${resolved.target}\n`;
358
424
  const testingNote = resolved.shouldIncludeTesting ? HEADLESS_COMPOSITOR_NOTE : "";
359
425
  p.note(`${cdStep}${devCmd}${testingNote}`, "Next steps");
360
426
  };
427
+ const createProjectStructure = async (root, resolved) => {
428
+ await runWithSpinner({
429
+ pending: "Creating project structure...",
430
+ done: "Project structure created",
431
+ failed: "Failed to create the project structure",
432
+ run: async () => {
433
+ prepareTargetDirectory(root, resolved.shouldEmptyTarget);
434
+ await scaffoldProject(root, resolved);
435
+ writeBuildAllowance(root, resolved.packageManager);
436
+ },
437
+ });
438
+ };
361
439
  const scaffold = async (options = {}) => {
362
440
  p.intro("Create GTKX App");
363
441
  const resolved = await resolveOptions(options);
364
- const root = resolve(process.cwd(), resolved.target);
442
+ const { root } = resolved;
365
443
  const devDeps = getDevDependencies(resolved);
366
- const projectSpinner = p.spinner();
367
- projectSpinner.start("Creating project structure...");
368
- await scaffoldProject(root, resolved);
369
- writeBuildAllowance(root, resolved.packageManager);
370
- projectSpinner.stop("Project structure created");
371
- await installAllDependencies({
372
- root,
373
- target: resolved.target,
374
- packageManager: resolved.packageManager,
375
- devDependencies: devDeps,
376
- });
444
+ await createProjectStructure(root, resolved);
445
+ await installAllDependencies({ resolved, devDependencies: devDeps });
377
446
  if (resolved.isTypescript) {
378
447
  writeInitialEnvModule(root);
379
448
  }