create-gtkx 1.6.0 → 2.0.0-beta.2
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 +5 -5
- package/dist/command.d.ts +5 -1
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +6 -1
- package/dist/command.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +10 -4
- package/dist/manifest.js.map +1 -1
- package/dist/scaffolder.d.ts +1 -0
- package/dist/scaffolder.d.ts.map +1 -1
- package/dist/scaffolder.js +171 -101
- package/dist/scaffolder.js.map +1 -1
- package/dist/templates/gtkx.config.ts.ejs +2 -12
- package/dist/templates/mise.toml.ejs +2 -0
- package/dist/templates/package.json.ejs +3 -0
- package/dist/templates/src/app.tsx.ejs +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +5 -7
- package/dist/templates.js.map +1 -1
- package/package.json +8 -6
- package/src/command.ts +7 -1
- package/src/manifest.ts +10 -4
- package/src/scaffolder.ts +230 -134
- package/src/templates/gtkx.config.ts.ejs +2 -12
- package/src/templates/mise.toml.ejs +2 -0
- package/src/templates/package.json.ejs +3 -0
- package/src/templates/src/app.tsx.ejs +1 -1
- package/src/templates.ts +6 -8
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="https://www.npmjs.com/package/create-gtkx"><img src="https://img.shields.io/npm/v/create-gtkx?color=cb3837&logo=npm&label=create-gtkx" alt="npm version" /></a>
|
|
15
15
|
<a href="https://www.npmjs.com/package/create-gtkx"><img src="https://img.shields.io/npm/dm/create-gtkx?color=cb3837&logo=npm&label=downloads" alt="npm downloads" /></a>
|
|
16
|
-
<img src="https://img.shields.io/badge/node-%E2%89%
|
|
16
|
+
<img src="https://img.shields.io/badge/node-%E2%89%A526.7-339933?logo=node.js&logoColor=white" alt="Node >= 26.7" />
|
|
17
17
|
<a href="https://github.com/gtkx-org/gtkx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MPL--2.0-blue.svg" alt="License: MPL-2.0" /></a>
|
|
18
18
|
<a href="https://github.com/gtkx-org/gtkx/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/gtkx-org/gtkx/ci.yml?branch=main&logo=github&label=CI" alt="CI status" /></a>
|
|
19
19
|
<img src="https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript&logoColor=white" alt="TypeScript" />
|
|
@@ -116,7 +116,7 @@ At runtime, the native Rust core calls straight into the system GTK4, Adwaita, a
|
|
|
116
116
|
|
|
117
117
|
## Quick start
|
|
118
118
|
|
|
119
|
-
GTKX is Linux-only and needs Node.js
|
|
119
|
+
GTKX is Linux-only and needs Node.js 26.7 or later. See [Requirements](#requirements).
|
|
120
120
|
|
|
121
121
|
Scaffold a new app with the `create-gtkx` initializer:
|
|
122
122
|
|
|
@@ -145,8 +145,8 @@ The documentation at **[gtkx.dev](https://gtkx.dev)** includes a step-by-step tu
|
|
|
145
145
|
|
|
146
146
|
GTKX is Linux-only. You need:
|
|
147
147
|
|
|
148
|
-
- Linux with the GTK4 (4.20 or later)
|
|
149
|
-
- Node.js
|
|
148
|
+
- Linux with the GTK4 (4.20 or later), Adwaita (1.8 or later), and GLib development libraries
|
|
149
|
+
- Node.js 26.7 or later
|
|
150
150
|
|
|
151
151
|
The `@gtkx/native` addon ships prebuilt for x64 and arm64 glibc Linux; other targets need to build it from the GTKX repository, which requires a Rust toolchain.
|
|
152
152
|
|
|
@@ -167,7 +167,7 @@ GTKX is stable and ready for production use.
|
|
|
167
167
|
|
|
168
168
|
## Contributing
|
|
169
169
|
|
|
170
|
-
Contributions are welcome. See [CONTRIBUTING.md](https://github.com/gtkx-org/gtkx/blob/main/CONTRIBUTING.md), the [Code of Conduct](https://github.com/gtkx-org/gtkx/blob/main/CODE_OF_CONDUCT.md), and the [security policy](https://github.com/gtkx-org/gtkx/blob/main/SECURITY.md). Building the repo needs Node.js
|
|
170
|
+
Contributions are welcome. See [CONTRIBUTING.md](https://github.com/gtkx-org/gtkx/blob/main/CONTRIBUTING.md), the [Code of Conduct](https://github.com/gtkx-org/gtkx/blob/main/CODE_OF_CONDUCT.md), and the [security policy](https://github.com/gtkx-org/gtkx/blob/main/SECURITY.md). Building the repo needs Node.js 26.7 or later, pnpm, and a Rust toolchain.
|
|
171
171
|
|
|
172
172
|
## License
|
|
173
173
|
|
package/dist/command.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ declare const scaffoldCommand: import("citty").CommandDef<{
|
|
|
8
8
|
readonly type: "string";
|
|
9
9
|
readonly description: "Application ID (e.g., com.example.myapp)";
|
|
10
10
|
};
|
|
11
|
+
readonly "display-name": {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly description: "Application display name (e.g., My App)";
|
|
14
|
+
};
|
|
11
15
|
readonly "package-manager": {
|
|
12
16
|
readonly type: "string";
|
|
13
17
|
readonly alias: "p";
|
|
@@ -34,7 +38,7 @@ declare const scaffoldCommand: import("citty").CommandDef<{
|
|
|
34
38
|
readonly overwrite: {
|
|
35
39
|
readonly type: "boolean";
|
|
36
40
|
readonly alias: "f";
|
|
37
|
-
readonly description: "
|
|
41
|
+
readonly description: "Replace scaffold files in a non-empty target directory when running without prompts";
|
|
38
42
|
};
|
|
39
43
|
}>;
|
|
40
44
|
export { scaffoldCommand };
|
package/dist/command.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAmBA,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDnB,CAAC;AAiCH,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/command.js
CHANGED
|
@@ -19,6 +19,10 @@ const scaffoldCommand = defineCommand({
|
|
|
19
19
|
type: "string",
|
|
20
20
|
description: "Application ID (e.g., com.example.myapp)",
|
|
21
21
|
},
|
|
22
|
+
"display-name": {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Application display name (e.g., My App)",
|
|
25
|
+
},
|
|
22
26
|
"package-manager": {
|
|
23
27
|
type: "string",
|
|
24
28
|
alias: "p",
|
|
@@ -46,7 +50,7 @@ const scaffoldCommand = defineCommand({
|
|
|
46
50
|
overwrite: {
|
|
47
51
|
type: "boolean",
|
|
48
52
|
alias: "f",
|
|
49
|
-
description: "
|
|
53
|
+
description: "Replace scaffold files in a non-empty target directory when running without prompts",
|
|
50
54
|
},
|
|
51
55
|
},
|
|
52
56
|
run: ({ args }) => runCreate(args),
|
|
@@ -66,6 +70,7 @@ const runCreate = async (args) => {
|
|
|
66
70
|
await scaffold({
|
|
67
71
|
name: args.name,
|
|
68
72
|
applicationId: args["application-id"],
|
|
73
|
+
displayName: args["display-name"],
|
|
69
74
|
packageManager: args["package-manager"],
|
|
70
75
|
isTypescript: args.typescript,
|
|
71
76
|
shouldIncludeTesting: args.vitest,
|
package/dist/command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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","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 \"display-name\"?: 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 \"display-name\": {\n type: \"string\",\n description: \"Application display name (e.g., My App)\",\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: \"Replace scaffold files in 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 displayName: args[\"display-name\"],\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"]}
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAAG,IAY3F,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/manifest.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
const PACKAGE_JSON_FILE = "package.json";
|
|
4
5
|
const updateManifest = (root, mutate) => {
|
|
5
6
|
const manifestPath = join(root, PACKAGE_JSON_FILE);
|
|
6
7
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
7
8
|
mutate(manifest);
|
|
8
|
-
const pendingPath =
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const pendingPath = join(root, `.package-${randomUUID()}.json`);
|
|
10
|
+
try {
|
|
11
|
+
writeFileSync(pendingPath, `${JSON.stringify(manifest, null, 4)}\n`, { flag: "wx", mode: 0o600 });
|
|
12
|
+
renameSync(pendingPath, manifestPath);
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
rmSync(pendingPath, { force: true });
|
|
16
|
+
}
|
|
11
17
|
};
|
|
12
18
|
export { updateManifest };
|
|
13
19
|
//# sourceMappingURL=manifest.js.map
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEzC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,MAAmD,EAAQ,EAAE;IAC/F,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAA4B,CAAC;IAC3F,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,UAAU,EAAE,OAAO,CAAC,CAAC;IAEhE,IAAI,CAAC;QACD,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClG,UAAU,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACP,MAAM,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;AACL,CAAC,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst PACKAGE_JSON_FILE = \"package.json\";\n\nconst updateManifest = (root: string, mutate: (manifest: Record<string, unknown>) => void): void => {\n const manifestPath = join(root, PACKAGE_JSON_FILE);\n const manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as Record<string, unknown>;\n mutate(manifest);\n const pendingPath = join(root, `.package-${randomUUID()}.json`);\n\n try {\n writeFileSync(pendingPath, `${JSON.stringify(manifest, null, 4)}\\n`, { flag: \"wx\", mode: 0o600 });\n renameSync(pendingPath, manifestPath);\n } finally {\n rmSync(pendingPath, { force: true });\n }\n};\n\nexport { updateManifest };\n"]}
|
package/dist/scaffolder.d.ts
CHANGED
package/dist/scaffolder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
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,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,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;AA0sBF,QAAA,MAAM,QAAQ,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAUjE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/scaffolder.js
CHANGED
|
@@ -2,8 +2,8 @@ import * as p from "@clack/prompts";
|
|
|
2
2
|
import { APPLICATION_ID_MAX_LENGTH, isValidApplicationId } from "@gtkx/config/internal";
|
|
3
3
|
import { errorMessage, tryResolveExecutable, upperFirst } from "@gtkx/utils";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import { mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { lstatSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { addDependency, detectPackageManager as nypmDetectPackageManager } from "nypm";
|
|
8
8
|
import { x } from "tinyexec";
|
|
9
9
|
import packageManifest from "../package.json" with { type: "json" };
|
|
@@ -39,10 +39,7 @@ const APPLICATION_ID_PREFIX = "com.";
|
|
|
39
39
|
const APPLICATION_ID_SUFFIX = ".app";
|
|
40
40
|
const APPLICATION_ID_SEGMENT_LIMIT = APPLICATION_ID_MAX_LENGTH - APPLICATION_ID_PREFIX.length -
|
|
41
41
|
APPLICATION_ID_SUFFIX.length;
|
|
42
|
-
const
|
|
43
|
-
const getDevCommand = (packageManager) => DEV_COMMAND[packageManager];
|
|
44
|
-
const getAddCommand = (packageManager) => ADD_COMMAND[packageManager];
|
|
45
|
-
const titleFromName = (name) => name.split("-").map((part) => upperFirst(part)).join(" ");
|
|
42
|
+
const displayNameFromProjectName = (name) => name.split("-").map((part) => upperFirst(part)).join(" ");
|
|
46
43
|
const gitConfigValue = (key) => {
|
|
47
44
|
const git = tryResolveExecutable("git");
|
|
48
45
|
if (git === undefined) {
|
|
@@ -74,6 +71,10 @@ const isDirEmpty = (dir) => {
|
|
|
74
71
|
const entries = readdirSync(dir);
|
|
75
72
|
return entries.length === 0 || (entries.length === 1 && entries[0] === ".git");
|
|
76
73
|
};
|
|
74
|
+
const shouldInitializeGit = (root) => {
|
|
75
|
+
const entry = lstatSync(root, { throwIfNoEntry: false });
|
|
76
|
+
return entry === undefined || (entry.isDirectory() && readdirSync(root).length === 0);
|
|
77
|
+
};
|
|
77
78
|
const getDevDependencies = ({ isTypescript, shouldIncludeTesting, }) => {
|
|
78
79
|
const devDeps = [...DEV_DEPENDENCIES];
|
|
79
80
|
if (isTypescript) {
|
|
@@ -103,6 +104,13 @@ const validateProjectName = (value) => {
|
|
|
103
104
|
const validateApplicationIdFormat = (value) => isValidApplicationId(value) ? undefined : APPLICATION_ID_FORMAT_ERROR;
|
|
104
105
|
const validateApplicationIdInput = (value) => value ? validateApplicationIdFormat(value) : "Application ID is required";
|
|
105
106
|
const validateApplicationIdAnswer = (value) => value ? validateApplicationIdFormat(value) : undefined;
|
|
107
|
+
const validateDisplayName = (value) => {
|
|
108
|
+
if (value === undefined || value.trim().length === 0) {
|
|
109
|
+
return "Display name is required";
|
|
110
|
+
}
|
|
111
|
+
return value.includes("\n") || value.includes("\r") ? "Display name must be a single line" : undefined;
|
|
112
|
+
};
|
|
113
|
+
const validateDisplayNameAnswer = (value) => value ? validateDisplayName(value) : undefined;
|
|
106
114
|
const fail = (message) => {
|
|
107
115
|
p.log.error(message);
|
|
108
116
|
throw new ScaffoldAbortedError(message);
|
|
@@ -116,14 +124,6 @@ const requestedPackageManager = (value) => {
|
|
|
116
124
|
}
|
|
117
125
|
return value;
|
|
118
126
|
};
|
|
119
|
-
const targetStats = (root) => {
|
|
120
|
-
try {
|
|
121
|
-
return statSync(root, { throwIfNoEntry: false });
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
127
|
const validateTargetDir = (target) => {
|
|
128
128
|
if (!target) {
|
|
129
129
|
return "Project directory is required";
|
|
@@ -145,29 +145,29 @@ const promptApplicationId = async (name) => {
|
|
|
145
145
|
validate: validateApplicationIdAnswer,
|
|
146
146
|
}));
|
|
147
147
|
};
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
const promptDisplayName = async (name) => {
|
|
149
|
+
const defaultDisplayName = displayNameFromProjectName(name);
|
|
150
|
+
return guardCancellation(await p.text({
|
|
151
|
+
message: "Display name",
|
|
152
|
+
placeholder: defaultDisplayName,
|
|
153
|
+
defaultValue: defaultDisplayName,
|
|
154
|
+
validate: validateDisplayNameAnswer,
|
|
155
|
+
}));
|
|
154
156
|
};
|
|
155
|
-
const
|
|
157
|
+
const packageManagerOption = (manager, detected) => {
|
|
158
|
+
let hint;
|
|
156
159
|
if (detected === manager.value) {
|
|
157
|
-
|
|
160
|
+
hint = "detected";
|
|
158
161
|
}
|
|
159
|
-
if (manager.isRecommended) {
|
|
160
|
-
|
|
162
|
+
else if (manager.isRecommended) {
|
|
163
|
+
hint = "recommended";
|
|
161
164
|
}
|
|
162
|
-
return undefined;
|
|
163
|
-
};
|
|
164
|
-
const packageManagerOption = (manager, detected) => {
|
|
165
|
-
const hint = packageManagerHint(manager, detected);
|
|
166
165
|
return { value: manager.value, label: manager.label, ...(hint !== undefined && { hint }) };
|
|
167
166
|
};
|
|
168
167
|
const detectedPackageManager = async () => {
|
|
169
168
|
try {
|
|
170
|
-
|
|
169
|
+
const detected = await nypmDetectPackageManager(process.cwd(), { includeParentDirs: true });
|
|
170
|
+
return detected && isKnownPackageManager(detected.name) ? detected.name : undefined;
|
|
171
171
|
}
|
|
172
172
|
catch {
|
|
173
173
|
return undefined;
|
|
@@ -182,52 +182,34 @@ const promptPackageManager = async () => {
|
|
|
182
182
|
initialValue: initial,
|
|
183
183
|
}));
|
|
184
184
|
};
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const shouldSetUpTesting = async () => guardCancellation(await p.confirm({
|
|
190
|
-
message: "Include testing setup (Vitest)?",
|
|
191
|
-
initialValue: true,
|
|
192
|
-
}));
|
|
193
|
-
const emptyDir = (root) => {
|
|
194
|
-
for (const entry of readdirSync(root)) {
|
|
195
|
-
if (entry === ".git") {
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
rmSync(join(root, entry), { recursive: true, force: true });
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
const prepareTargetDirectory = (root, shouldEmptyTarget) => {
|
|
202
|
-
mkdirSync(root, { recursive: true });
|
|
203
|
-
if (shouldEmptyTarget) {
|
|
204
|
-
emptyDir(root);
|
|
205
|
-
}
|
|
206
|
-
};
|
|
185
|
+
const isOptionEnabled = async (value, isInteractive, message) => value ??
|
|
186
|
+
(isInteractive
|
|
187
|
+
? guardCancellation(await p.confirm({ message, initialValue: true }))
|
|
188
|
+
: true);
|
|
207
189
|
const shouldOverwriteDirectory = async (target, options) => {
|
|
208
190
|
if (!options.isInteractive) {
|
|
209
191
|
return options.shouldOverwrite === true;
|
|
210
192
|
}
|
|
211
193
|
return guardCancellation(await p.confirm({
|
|
212
|
-
message: `Directory "${target}" is not empty.
|
|
194
|
+
message: `Directory "${target}" is not empty. Replace scaffold files?`,
|
|
213
195
|
initialValue: false,
|
|
214
196
|
}));
|
|
215
197
|
};
|
|
216
|
-
const
|
|
217
|
-
const stats =
|
|
198
|
+
const validateTargetAvailability = async (root, target, options) => {
|
|
199
|
+
const stats = statSync(root, { throwIfNoEntry: false });
|
|
218
200
|
if (stats === undefined) {
|
|
219
|
-
return
|
|
201
|
+
return;
|
|
220
202
|
}
|
|
221
203
|
if (!stats.isDirectory()) {
|
|
222
204
|
return fail(`Target "${target}" is not a directory`);
|
|
223
205
|
}
|
|
224
206
|
if (isDirEmpty(root)) {
|
|
225
|
-
return
|
|
207
|
+
return;
|
|
226
208
|
}
|
|
227
209
|
if (await shouldOverwriteDirectory(target, options)) {
|
|
228
|
-
return
|
|
210
|
+
return;
|
|
229
211
|
}
|
|
230
|
-
|
|
212
|
+
fail(`Directory "${target}" is not empty`);
|
|
231
213
|
};
|
|
232
214
|
const resolveTarget = async (options) => {
|
|
233
215
|
if (options.name === undefined && options.isInteractive) {
|
|
@@ -243,6 +225,12 @@ const resolveApplicationId = async (options, name) => {
|
|
|
243
225
|
const error = validateApplicationIdInput(applicationId);
|
|
244
226
|
return error === undefined ? applicationId : fail(error);
|
|
245
227
|
};
|
|
228
|
+
const resolveDisplayName = async (options, name) => {
|
|
229
|
+
const value = options.displayName ??
|
|
230
|
+
(options.isInteractive ? await promptDisplayName(name) : displayNameFromProjectName(name));
|
|
231
|
+
const error = validateDisplayName(value);
|
|
232
|
+
return error === undefined ? value.trim() : fail(error);
|
|
233
|
+
};
|
|
246
234
|
const resolvePackageManager = async (requested, options) => {
|
|
247
235
|
if (requested !== undefined) {
|
|
248
236
|
return requested;
|
|
@@ -252,39 +240,30 @@ const resolvePackageManager = async (requested, options) => {
|
|
|
252
240
|
}
|
|
253
241
|
return (await detectedPackageManager()) ?? "pnpm";
|
|
254
242
|
};
|
|
255
|
-
const isTypescriptSelected = async (options) => {
|
|
256
|
-
if (options.isTypescript !== undefined) {
|
|
257
|
-
return options.isTypescript;
|
|
258
|
-
}
|
|
259
|
-
return options.isInteractive ? shouldUseTypeScript() : true;
|
|
260
|
-
};
|
|
261
|
-
const isTestingIncluded = async (options) => {
|
|
262
|
-
if (options.shouldIncludeTesting !== undefined) {
|
|
263
|
-
return options.shouldIncludeTesting;
|
|
264
|
-
}
|
|
265
|
-
return options.isInteractive ? shouldSetUpTesting() : true;
|
|
266
|
-
};
|
|
267
243
|
const resolveOptions = async (options) => {
|
|
268
244
|
const requested = requestedPackageManager(options.packageManager);
|
|
269
245
|
const target = await resolveTarget(options);
|
|
270
246
|
const name = deriveProjectName(target);
|
|
247
|
+
const displayName = await resolveDisplayName(options, name);
|
|
271
248
|
const applicationId = await resolveApplicationId(options, name);
|
|
272
249
|
const root = resolve(process.cwd(), target);
|
|
273
250
|
const isCurrentDirectory = root === process.cwd();
|
|
274
|
-
const
|
|
251
|
+
const shouldInitialize = shouldInitializeGit(root);
|
|
252
|
+
await validateTargetAvailability(root, target, options);
|
|
275
253
|
const packageManager = await resolvePackageManager(requested, options);
|
|
276
|
-
const isTypescript = await
|
|
277
|
-
const shouldIncludeTesting = await
|
|
254
|
+
const isTypescript = await isOptionEnabled(options.isTypescript, options.isInteractive, "Use TypeScript?");
|
|
255
|
+
const shouldIncludeTesting = await isOptionEnabled(options.shouldIncludeTesting, options.isInteractive, "Include testing setup (Vitest)?");
|
|
278
256
|
return {
|
|
279
257
|
target,
|
|
280
258
|
root,
|
|
281
259
|
isCurrentDirectory,
|
|
282
260
|
name,
|
|
261
|
+
displayName,
|
|
283
262
|
applicationId,
|
|
284
263
|
packageManager,
|
|
285
264
|
isTypescript,
|
|
286
265
|
shouldIncludeTesting,
|
|
287
|
-
|
|
266
|
+
shouldInitializeGit: shouldInitialize,
|
|
288
267
|
};
|
|
289
268
|
};
|
|
290
269
|
const isTemplateIncluded = (templateRelativePath, resolved) => {
|
|
@@ -296,37 +275,112 @@ const isTemplateIncluded = (templateRelativePath, resolved) => {
|
|
|
296
275
|
}
|
|
297
276
|
return true;
|
|
298
277
|
};
|
|
299
|
-
const
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
manifest.scripts.test = "vitest run";
|
|
305
|
-
});
|
|
278
|
+
const destinationPath = (template, resolved) => {
|
|
279
|
+
const scriptPath = resolved.isTypescript ? template : template.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".js");
|
|
280
|
+
return scriptPath.endsWith(ICON_TEMPLATE_NAME)
|
|
281
|
+
? `${scriptPath.slice(0, -ICON_TEMPLATE_NAME.length)}${resolved.applicationId}.svg`
|
|
282
|
+
: scriptPath;
|
|
306
283
|
};
|
|
307
284
|
const templateContext = (resolved) => ({
|
|
308
285
|
name: resolved.name,
|
|
309
286
|
applicationId: resolved.applicationId,
|
|
310
|
-
|
|
287
|
+
displayName: resolved.displayName,
|
|
311
288
|
shouldIncludeTesting: resolved.shouldIncludeTesting,
|
|
312
289
|
isTypescript: resolved.isTypescript,
|
|
313
290
|
importExtension: resolved.isTypescript ? ".js" : ".jsx",
|
|
314
|
-
developerName: gitConfigValue("user.name") ??
|
|
291
|
+
developerName: gitConfigValue("user.name") ?? resolved.displayName,
|
|
315
292
|
developerEmail: gitConfigValue("user.email"),
|
|
316
293
|
});
|
|
317
|
-
const
|
|
318
|
-
const
|
|
294
|
+
const replaceTemplateDestination = (destination) => {
|
|
295
|
+
const entry = lstatSync(destination, { throwIfNoEntry: false });
|
|
296
|
+
if (entry === undefined) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) {
|
|
300
|
+
fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
|
|
301
|
+
}
|
|
302
|
+
rmSync(destination, { force: true });
|
|
303
|
+
};
|
|
304
|
+
const projectRelativeDestination = (root, destination) => {
|
|
305
|
+
const relativePath = relative(root, destination);
|
|
306
|
+
const isOutsideProject = relativePath.length === 0 || relativePath.startsWith(`..${sep}`) ||
|
|
307
|
+
isAbsolute(relativePath);
|
|
308
|
+
if (isOutsideProject) {
|
|
309
|
+
fail(`Scaffold destination is outside the project: ${destination}`);
|
|
310
|
+
}
|
|
311
|
+
return relativePath;
|
|
312
|
+
};
|
|
313
|
+
const assertRootIsNotSymbolicLink = (root) => {
|
|
314
|
+
const rootEntry = lstatSync(root, { throwIfNoEntry: false });
|
|
315
|
+
if (rootEntry?.isSymbolicLink()) {
|
|
316
|
+
fail(`Cannot scaffold through a symbolic link: ${root}`);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const assertSafeAncestor = (ancestor) => {
|
|
320
|
+
const entry = lstatSync(ancestor, { throwIfNoEntry: false });
|
|
321
|
+
if (entry?.isSymbolicLink()) {
|
|
322
|
+
fail(`Cannot scaffold through a symbolic link: ${ancestor}`);
|
|
323
|
+
}
|
|
324
|
+
if (entry !== undefined && !entry.isDirectory()) {
|
|
325
|
+
fail(`Cannot scaffold through a non-directory path: ${ancestor}`);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
const assertSafeAncestors = (root, relativePath) => {
|
|
329
|
+
let ancestor = root;
|
|
330
|
+
const segments = relativePath.split(sep);
|
|
331
|
+
const ancestorSegments = segments.slice(0, -1);
|
|
332
|
+
for (const segment of ancestorSegments) {
|
|
333
|
+
ancestor = join(ancestor, segment);
|
|
334
|
+
assertSafeAncestor(ancestor);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const assertReplaceableDestination = (destination) => {
|
|
338
|
+
const entry = lstatSync(destination, { throwIfNoEntry: false });
|
|
339
|
+
const isReplaceable = entry === undefined || entry.isFile() || entry.isSymbolicLink();
|
|
340
|
+
if (!isReplaceable) {
|
|
341
|
+
fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
const assertSafeDestination = (root, destination) => {
|
|
345
|
+
const relativePath = projectRelativeDestination(root, destination);
|
|
346
|
+
assertRootIsNotSymbolicLink(root);
|
|
347
|
+
assertSafeAncestors(root, relativePath);
|
|
348
|
+
assertReplaceableDestination(destination);
|
|
349
|
+
};
|
|
350
|
+
const plannedScaffoldFiles = async (root, resolved) => {
|
|
319
351
|
const context = templateContext(resolved);
|
|
352
|
+
const files = [];
|
|
320
353
|
for (const template of listTemplates()) {
|
|
321
|
-
if (
|
|
322
|
-
|
|
354
|
+
if (isTemplateIncluded(template, resolved)) {
|
|
355
|
+
files.push({
|
|
356
|
+
destination: join(root, destinationPath(template, resolved)),
|
|
357
|
+
contents: await renderFile(template, context),
|
|
358
|
+
});
|
|
323
359
|
}
|
|
324
|
-
const destination = join(root, toDestinationPath(template, isTypescript, applicationId));
|
|
325
|
-
mkdirSync(dirname(destination), { recursive: true });
|
|
326
|
-
writeFileSync(destination, await renderFile(template, context));
|
|
327
360
|
}
|
|
328
|
-
|
|
329
|
-
|
|
361
|
+
return files;
|
|
362
|
+
};
|
|
363
|
+
const auxiliaryDestinations = (root, resolved) => [
|
|
364
|
+
...(resolved.packageManager === "pnpm" ? [join(root, "pnpm-workspace.yaml")] : []),
|
|
365
|
+
...(resolved.isTypescript ? [join(root, "node_modules", ".gtkx", "env.d.ts")] : []),
|
|
366
|
+
];
|
|
367
|
+
const scaffoldProject = async (root, resolved) => {
|
|
368
|
+
const files = await plannedScaffoldFiles(root, resolved);
|
|
369
|
+
const destinations = [...files.map((file) => file.destination), ...auxiliaryDestinations(root, resolved)];
|
|
370
|
+
for (const destination of destinations) {
|
|
371
|
+
assertSafeDestination(root, destination);
|
|
372
|
+
}
|
|
373
|
+
for (const destination of destinations) {
|
|
374
|
+
replaceTemplateDestination(destination);
|
|
375
|
+
}
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
mkdirSync(dirname(file.destination), { recursive: true });
|
|
378
|
+
writeFileSync(file.destination, file.contents);
|
|
379
|
+
}
|
|
380
|
+
if (resolved.shouldIncludeTesting) {
|
|
381
|
+
updateManifest(root, (manifest) => {
|
|
382
|
+
manifest.scripts.test = "vitest run";
|
|
383
|
+
});
|
|
330
384
|
}
|
|
331
385
|
};
|
|
332
386
|
const withoutInheritedScriptPolicy = async (run) => {
|
|
@@ -351,9 +405,9 @@ const installDependencies = async (options) => {
|
|
|
351
405
|
await addDependency(dependencies, { cwd, packageManager, dev: isDev, silent: true });
|
|
352
406
|
});
|
|
353
407
|
};
|
|
354
|
-
const pin = (names) => names.map((dependency) =>
|
|
408
|
+
const pin = (names) => names.map((dependency) => dependency.startsWith("@gtkx/") ? `${dependency}@^${selfVersion}` : dependency);
|
|
355
409
|
const formatRecovery = ({ resolved, devDependencies }) => {
|
|
356
|
-
const add =
|
|
410
|
+
const add = ADD_COMMAND[resolved.packageManager];
|
|
357
411
|
const steps = [
|
|
358
412
|
...(resolved.isCurrentDirectory ? [] : [`cd ${resolved.target}`]),
|
|
359
413
|
`${add} ${pin(DEPENDENCIES).join(" ")}`,
|
|
@@ -404,7 +458,23 @@ const writeInitialEnvModule = (root) => {
|
|
|
404
458
|
mkdirSync(storeDir, { recursive: true });
|
|
405
459
|
writeFileSync(join(storeDir, "env.d.ts"), `${GTKX_ENV_MODULE_HEADER}\n`);
|
|
406
460
|
};
|
|
407
|
-
const
|
|
461
|
+
const isInsideGitRepository = (root) => {
|
|
462
|
+
const git = tryResolveExecutable("git");
|
|
463
|
+
if (git === undefined) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
try {
|
|
467
|
+
execFileSync(git, ["-C", root, "rev-parse", "--show-toplevel"], { stdio: "ignore" });
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
const initializeGitRepo = async (root, shouldInitialize) => {
|
|
475
|
+
if (!shouldInitialize || isInsideGitRepository(root)) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
408
478
|
const spinner = p.spinner();
|
|
409
479
|
spinner.start("Initializing git repository...");
|
|
410
480
|
try {
|
|
@@ -419,7 +489,7 @@ const initializeGitRepo = async (root) => {
|
|
|
419
489
|
}
|
|
420
490
|
};
|
|
421
491
|
const printNextSteps = (resolved) => {
|
|
422
|
-
const devCmd =
|
|
492
|
+
const devCmd = DEV_COMMAND[resolved.packageManager];
|
|
423
493
|
const cdStep = resolved.isCurrentDirectory ? "" : `cd ${resolved.target}\n`;
|
|
424
494
|
const testingNote = resolved.shouldIncludeTesting ? HEADLESS_COMPOSITOR_NOTE : "";
|
|
425
495
|
p.note(`${cdStep}${devCmd}${testingNote}`, "Next steps");
|
|
@@ -430,9 +500,12 @@ const createProjectStructure = async (root, resolved) => {
|
|
|
430
500
|
done: "Project structure created",
|
|
431
501
|
failed: "Failed to create the project structure",
|
|
432
502
|
run: async () => {
|
|
433
|
-
|
|
503
|
+
mkdirSync(root, { recursive: true });
|
|
434
504
|
await scaffoldProject(root, resolved);
|
|
435
505
|
writeBuildAllowance(root, resolved.packageManager);
|
|
506
|
+
if (resolved.isTypescript) {
|
|
507
|
+
writeInitialEnvModule(root);
|
|
508
|
+
}
|
|
436
509
|
},
|
|
437
510
|
});
|
|
438
511
|
};
|
|
@@ -443,10 +516,7 @@ const scaffold = async (options = {}) => {
|
|
|
443
516
|
const devDeps = getDevDependencies(resolved);
|
|
444
517
|
await createProjectStructure(root, resolved);
|
|
445
518
|
await installAllDependencies({ resolved, devDependencies: devDeps });
|
|
446
|
-
|
|
447
|
-
writeInitialEnvModule(root);
|
|
448
|
-
}
|
|
449
|
-
await initializeGitRepo(root);
|
|
519
|
+
await initializeGitRepo(root, resolved.shouldInitializeGit);
|
|
450
520
|
printNextSteps(resolved);
|
|
451
521
|
};
|
|
452
522
|
export { scaffold };
|