@ts-for-gir/cli 4.0.0-rc.3 → 4.0.0-rc.5
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/bin/ts-for-gir +2325 -491
- package/dist-templates/types-locally/.ts-for-girrc.js +6 -0
- package/dist-templates/types-locally/README.md +15 -0
- package/dist-templates/types-locally/esbuild.ts +10 -0
- package/dist-templates/types-locally/main.ts +21 -0
- package/dist-templates/types-locally/package.json +18 -0
- package/dist-templates/types-locally/tsconfig.json +17 -0
- package/dist-templates/types-npm/README.md +14 -0
- package/dist-templates/types-npm/esbuild.ts +10 -0
- package/dist-templates/types-npm/main.ts +19 -0
- package/dist-templates/types-npm/package.json +23 -0
- package/dist-templates/types-npm/tsconfig.json +15 -0
- package/dist-templates/types-workspace/.ts-for-girrc.js +12 -0
- package/dist-templates/types-workspace/README.md +26 -0
- package/dist-templates/types-workspace/package.json +22 -0
- package/dist-templates/types-workspace/packages/app/esbuild.ts +10 -0
- package/dist-templates/types-workspace/packages/app/main.ts +19 -0
- package/dist-templates/types-workspace/packages/app/package.json +23 -0
- package/dist-templates/types-workspace/packages/app/tsconfig.json +15 -0
- package/dist-templates/types-workspace/tsconfig.json +11 -0
- package/package.json +21 -18
- package/src/commands/create.ts +214 -0
- package/src/commands/index.ts +1 -0
- package/src/config/config-loader.ts +1 -0
- package/src/config/index.ts +9 -1
- package/src/config/options.ts +27 -0
- package/src/start.ts +2 -1
- package/src/types/command-args.ts +23 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
GJS + TypeScript starter that generates GObject Introspection types **locally** into `./@types/`, without using the `@girs/*` NPM packages.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install
|
|
9
|
+
npm run generate # writes types into ./@types/
|
|
10
|
+
npm run check:types # tsc --noEmit
|
|
11
|
+
npm run build # bundles main.ts to dist/ via esbuild
|
|
12
|
+
npm start # runs dist/main.js with gjs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`@types/index.d.ts` is regenerated each time you run `npm run generate`. Add or remove modules in `.ts-for-girrc.js`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference path="./@types/index.d.ts" />
|
|
2
|
+
|
|
3
|
+
import Adw from "gi://Adw?version=1";
|
|
4
|
+
import Gio from "gi://Gio?version=2.0";
|
|
5
|
+
import Gtk from "gi://Gtk?version=4.0";
|
|
6
|
+
|
|
7
|
+
const app = new Adw.Application({
|
|
8
|
+
applicationId: "com.example.__PROJECT_NAME__",
|
|
9
|
+
flags: Gio.ApplicationFlags.FLAGS_NONE,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
app.connect("activate", (app: Adw.Application) => {
|
|
13
|
+
const label = new Gtk.Label({ label: "Hello from __PROJECT_NAME__" });
|
|
14
|
+
const window = new Gtk.ApplicationWindow({ application: app });
|
|
15
|
+
window.set_title("__PROJECT_NAME__");
|
|
16
|
+
window.set_default_size(320, 120);
|
|
17
|
+
window.set_child(label);
|
|
18
|
+
window.present();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
app.run([imports.system.programInvocationName].concat(ARGV));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"generate": "ts-for-gir generate",
|
|
8
|
+
"check:types": "tsc --noEmit",
|
|
9
|
+
"build": "node --experimental-strip-types --experimental-transform-types --no-warnings esbuild.ts",
|
|
10
|
+
"start": "gjs -m dist/main.js",
|
|
11
|
+
"clear": "rm -rf dist @types"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@ts-for-gir/cli": "^4.0.0-rc.5",
|
|
15
|
+
"esbuild": "^0.28.0",
|
|
16
|
+
"typescript": "^6.0.2"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ESNext"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"skipLibCheck": false,
|
|
8
|
+
"types": [],
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noImplicitAny": true,
|
|
11
|
+
"strictNullChecks": true,
|
|
12
|
+
"noImplicitThis": true,
|
|
13
|
+
"alwaysStrict": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["main.ts", "@types/index.d.ts"],
|
|
16
|
+
"exclude": []
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
GJS + TypeScript starter that consumes pre-generated GObject Introspection types from the [`@girs/*`](https://www.npmjs.com/org/girs) NPM packages.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install
|
|
9
|
+
npm run check # tsc --noEmit
|
|
10
|
+
npm run build # bundles main.ts to dist/ via esbuild
|
|
11
|
+
npm start # runs dist/main.js with gjs
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
To add another GIR module, install the matching `@girs/<name>-<version>` package and add it to `tsconfig.json`'s `types` array.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Adw from "gi://Adw?version=1";
|
|
2
|
+
import Gio from "gi://Gio?version=2.0";
|
|
3
|
+
import Gtk from "gi://Gtk?version=4.0";
|
|
4
|
+
|
|
5
|
+
const app = new Adw.Application({
|
|
6
|
+
applicationId: "com.example.__PROJECT_NAME__",
|
|
7
|
+
flags: Gio.ApplicationFlags.FLAGS_NONE,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
app.connect("activate", (app: Adw.Application) => {
|
|
11
|
+
const label = new Gtk.Label({ label: "Hello from __PROJECT_NAME__" });
|
|
12
|
+
const window = new Gtk.ApplicationWindow({ application: app });
|
|
13
|
+
window.set_title("__PROJECT_NAME__");
|
|
14
|
+
window.set_default_size(320, 120);
|
|
15
|
+
window.set_child(label);
|
|
16
|
+
window.present();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.run([imports.system.programInvocationName].concat(ARGV));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"check": "tsc --noEmit",
|
|
8
|
+
"build": "node --experimental-strip-types --experimental-transform-types --no-warnings esbuild.ts",
|
|
9
|
+
"start": "gjs -m dist/main.js",
|
|
10
|
+
"clear": "rm -rf dist"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"esbuild": "^0.28.0",
|
|
14
|
+
"typescript": "^6.0.2"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@girs/adw-1": "^1.10.0-4.0.0-rc.4",
|
|
18
|
+
"@girs/gio-2.0": "^2.88.0-4.0.0-rc.4",
|
|
19
|
+
"@girs/gjs": "^4.0.0-rc.4",
|
|
20
|
+
"@girs/glib-2.0": "^2.88.0-4.0.0-rc.4",
|
|
21
|
+
"@girs/gtk-4.0": "^4.23.0-4.0.0-rc.4"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"types": ["@girs/gjs", "@girs/adw-1"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noImplicitAny": true,
|
|
10
|
+
"strictNullChecks": true,
|
|
11
|
+
"noImplicitThis": true,
|
|
12
|
+
"alwaysStrict": true
|
|
13
|
+
},
|
|
14
|
+
"files": ["main.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
modules: ["Adw-1", "Gtk-4.0"],
|
|
3
|
+
outdir: "./@girs",
|
|
4
|
+
package: true,
|
|
5
|
+
// npm does not support the workspace: protocol. `caret` emits ^<version>
|
|
6
|
+
// which npm, yarn and pnpm all resolve to the local workspace when the ref
|
|
7
|
+
// matches, falling back to the registry otherwise — the most portable
|
|
8
|
+
// default for a generated monorepo. Falls back to "exact" on CLI versions
|
|
9
|
+
// that predate this option, which is also npm-compatible.
|
|
10
|
+
depVersionFormat: "caret",
|
|
11
|
+
ignoreVersionConflicts: true,
|
|
12
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
GJS + TypeScript starter that uses an **npm workspace** with the `@girs/*` types generated locally as workspace packages. Useful when you want pinned, hermetic types under version control without depending on the published `@girs/*` packages on NPM.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install
|
|
9
|
+
npm run build:types # generates @girs/* packages into ./@girs/
|
|
10
|
+
npm install # picks up the freshly generated @girs/* workspace packages
|
|
11
|
+
npm run build:app
|
|
12
|
+
npm start
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or shorthand: `npm run build` runs all three steps in order.
|
|
16
|
+
|
|
17
|
+
The application lives in [`packages/app/`](./packages/app/). Its dependencies on `@girs/*` are written as `"*"` which works across all three package managers — npm, yarn, pnpm all resolve to the locally generated workspace packages in `./@girs/`.
|
|
18
|
+
|
|
19
|
+
### About the dependency version format
|
|
20
|
+
|
|
21
|
+
Two deliberate choices keep the template portable:
|
|
22
|
+
|
|
23
|
+
1. **Generated `@girs/*` packages** reference each other via `^<version>` (not `workspace:^`). Controlled by `depVersionFormat: "caret"` in [`.ts-for-girrc.js`](./.ts-for-girrc.js). npm and yarn/pnpm all prefer the local workspace match; the registry serves as fallback for transitive GIR packages outside your `modules` set.
|
|
24
|
+
2. **Sub-package deps** (`packages/app/package.json`) use `"*"`. Same reasoning — all managers resolve to the local workspace.
|
|
25
|
+
|
|
26
|
+
If you run yarn or pnpm exclusively and want strict workspace-only resolution, switch both: add `workspace: true` and `depVersionFormat: "workspace"` in `.ts-for-girrc.js`, plus `"workspace:^"` specs in `packages/app/package.json`. npm does **not** support the `workspace:` protocol.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"workspaces": [
|
|
7
|
+
"packages/*",
|
|
8
|
+
"@girs/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build:types": "ts-for-gir generate --package --outdir=./@girs",
|
|
12
|
+
"build:app": "npm run --workspace=@__PROJECT_NAME__/app build",
|
|
13
|
+
"build": "npm run build:types && npm install && npm run build:app",
|
|
14
|
+
"check": "npm run --workspaces --if-present check",
|
|
15
|
+
"start": "npm run --workspace=@__PROJECT_NAME__/app start",
|
|
16
|
+
"clear": "rm -rf @girs packages/*/dist"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@ts-for-gir/cli": "^4.0.0-rc.5",
|
|
20
|
+
"typescript": "^6.0.2"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Adw from "gi://Adw?version=1";
|
|
2
|
+
import Gio from "gi://Gio?version=2.0";
|
|
3
|
+
import Gtk from "gi://Gtk?version=4.0";
|
|
4
|
+
|
|
5
|
+
const app = new Adw.Application({
|
|
6
|
+
applicationId: "com.example.__PROJECT_NAME__",
|
|
7
|
+
flags: Gio.ApplicationFlags.FLAGS_NONE,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
app.connect("activate", (app: Adw.Application) => {
|
|
11
|
+
const label = new Gtk.Label({ label: "Hello from __PROJECT_NAME__" });
|
|
12
|
+
const window = new Gtk.ApplicationWindow({ application: app });
|
|
13
|
+
window.set_title("__PROJECT_NAME__");
|
|
14
|
+
window.set_default_size(320, 120);
|
|
15
|
+
window.set_child(label);
|
|
16
|
+
window.present();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.run([imports.system.programInvocationName].concat(ARGV));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@__PROJECT_NAME__/app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"check": "tsc --noEmit",
|
|
8
|
+
"build": "node --experimental-strip-types --experimental-transform-types --no-warnings esbuild.ts",
|
|
9
|
+
"start": "gjs -m dist/main.js",
|
|
10
|
+
"clear": "rm -rf dist"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"esbuild": "^0.28.0",
|
|
14
|
+
"typescript": "^6.0.2"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@girs/adw-1": "*",
|
|
18
|
+
"@girs/gio-2.0": "*",
|
|
19
|
+
"@girs/gjs": "*",
|
|
20
|
+
"@girs/glib-2.0": "*",
|
|
21
|
+
"@girs/gtk-4.0": "*"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"types": ["@girs/gjs", "@girs/adw-1"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noImplicitAny": true,
|
|
10
|
+
"strictNullChecks": true,
|
|
11
|
+
"noImplicitThis": true,
|
|
12
|
+
"alwaysStrict": true
|
|
13
|
+
},
|
|
14
|
+
"files": ["main.ts"]
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-for-gir/cli",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.5",
|
|
4
4
|
"description": "TypeScript type definition generator for GObject introspection GIR files",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"start": "node bin/ts-for-gir-dev",
|
|
18
18
|
"start:prod": "node bin/ts-for-gir",
|
|
19
19
|
"build": "node --experimental-specifier-resolution=node --experimental-strip-types --experimental-transform-types --no-warnings esbuild.ts && chmod +x bin/ts-for-gir-dev && chmod +x bin/ts-for-gir",
|
|
20
|
+
"build:templates": "node scripts/process-templates.mjs",
|
|
21
|
+
"prepack": "node scripts/process-templates.mjs",
|
|
20
22
|
"check:types": "tsc --noEmit",
|
|
21
23
|
"check": "yarn check:types"
|
|
22
24
|
},
|
|
@@ -27,7 +29,8 @@
|
|
|
27
29
|
"author": "Pascal Garber <pascal@mailfreun.de>",
|
|
28
30
|
"files": [
|
|
29
31
|
"src",
|
|
30
|
-
"bin"
|
|
32
|
+
"bin",
|
|
33
|
+
"dist-templates"
|
|
31
34
|
],
|
|
32
35
|
"license": "Apache-2.0",
|
|
33
36
|
"bugs": {
|
|
@@ -52,31 +55,31 @@
|
|
|
52
55
|
".": "./src/index.ts"
|
|
53
56
|
},
|
|
54
57
|
"devDependencies": {
|
|
55
|
-
"@gi.ts/parser": "^4.0.0-rc.
|
|
56
|
-
"@ts-for-gir/generator-base": "^4.0.0-rc.
|
|
57
|
-
"@ts-for-gir/generator-html-doc": "^4.0.0-rc.
|
|
58
|
-
"@ts-for-gir/generator-json": "^4.0.0-rc.
|
|
59
|
-
"@ts-for-gir/generator-typescript": "^4.0.0-rc.
|
|
60
|
-
"@ts-for-gir/lib": "^4.0.0-rc.
|
|
61
|
-
"@ts-for-gir/reporter": "^4.0.0-rc.
|
|
62
|
-
"@ts-for-gir/tsconfig": "^4.0.0-rc.
|
|
58
|
+
"@gi.ts/parser": "^4.0.0-rc.5",
|
|
59
|
+
"@ts-for-gir/generator-base": "^4.0.0-rc.5",
|
|
60
|
+
"@ts-for-gir/generator-html-doc": "^4.0.0-rc.5",
|
|
61
|
+
"@ts-for-gir/generator-json": "^4.0.0-rc.5",
|
|
62
|
+
"@ts-for-gir/generator-typescript": "^4.0.0-rc.5",
|
|
63
|
+
"@ts-for-gir/lib": "^4.0.0-rc.5",
|
|
64
|
+
"@ts-for-gir/reporter": "^4.0.0-rc.5",
|
|
65
|
+
"@ts-for-gir/tsconfig": "^4.0.0-rc.5",
|
|
63
66
|
"@types/ejs": "^3.1.5",
|
|
64
67
|
"@types/inquirer": "^9.0.9",
|
|
65
|
-
"@types/node": "^
|
|
68
|
+
"@types/node": "^25.6.0",
|
|
66
69
|
"@types/yargs": "^17.0.35",
|
|
67
70
|
"esbuild": "^0.28.0",
|
|
68
|
-
"typescript": "^6.0.
|
|
71
|
+
"typescript": "^6.0.3"
|
|
69
72
|
},
|
|
70
73
|
"dependencies": {
|
|
71
|
-
"@inquirer/prompts": "^8.
|
|
72
|
-
"@ts-for-gir/templates": "^4.0.0-rc.
|
|
74
|
+
"@inquirer/prompts": "^8.4.2",
|
|
75
|
+
"@ts-for-gir/templates": "^4.0.0-rc.5",
|
|
73
76
|
"colorette": "^2.0.20",
|
|
74
77
|
"cosmiconfig": "^9.0.1",
|
|
75
|
-
"ejs": "^5.0.
|
|
78
|
+
"ejs": "^5.0.2",
|
|
76
79
|
"glob": "^13.0.6",
|
|
77
|
-
"inquirer": "^13.
|
|
78
|
-
"prettier": "^3.8.
|
|
79
|
-
"typedoc": "^0.28.
|
|
80
|
+
"inquirer": "^13.4.2",
|
|
81
|
+
"prettier": "^3.8.3",
|
|
82
|
+
"typedoc": "^0.28.19",
|
|
80
83
|
"yargs": "^18.0.0"
|
|
81
84
|
}
|
|
82
85
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything you need for the `ts-for-gir create` command is located here
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
import { input, select } from "@inquirer/prompts";
|
|
11
|
+
import { APP_NAME, type ConfigFlags, Logger } from "@ts-for-gir/lib";
|
|
12
|
+
|
|
13
|
+
import { createOptions } from "../config.ts";
|
|
14
|
+
import type { CreateCommandArgs, CreateTemplateId } from "../types/index.ts";
|
|
15
|
+
import { createBuilder } from "./command-builder.ts";
|
|
16
|
+
|
|
17
|
+
const command = "create [name]";
|
|
18
|
+
|
|
19
|
+
const description = "Scaffold a new GJS TypeScript project from a template";
|
|
20
|
+
|
|
21
|
+
const examples: ReadonlyArray<[string, string?]> = [
|
|
22
|
+
[`${APP_NAME} create my-app --template types-npm`, "Scaffold using the @girs/* NPM types"],
|
|
23
|
+
[`${APP_NAME} create my-app --template types-locally`, "Scaffold and generate types into ./@types/ locally"],
|
|
24
|
+
[
|
|
25
|
+
`${APP_NAME} create my-app --template types-workspace`,
|
|
26
|
+
"Scaffold an npm workspace with types as workspace packages",
|
|
27
|
+
],
|
|
28
|
+
[`${APP_NAME} create`, "Interactive: prompts for name and template"],
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const TEMPLATE_CHOICES: ReadonlyArray<{ value: CreateTemplateId; name: string; description: string }> = [
|
|
32
|
+
{
|
|
33
|
+
value: "types-locally",
|
|
34
|
+
name: "types-locally",
|
|
35
|
+
description: "Generate GIR types directly into ./@types/ (no package format, no @girs/* deps)",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
value: "types-npm",
|
|
39
|
+
name: "types-npm",
|
|
40
|
+
description: "Use pre-generated types from the @girs/* NPM packages",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: "types-workspace",
|
|
44
|
+
name: "types-workspace",
|
|
45
|
+
description: "npm workspace; generate @girs/* as workspace packages under ./@girs/",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__";
|
|
50
|
+
|
|
51
|
+
const TEXT_FILE_EXT = new Set([".json", ".md", ".ts", ".tsx", ".js", ".mjs", ".cjs"]);
|
|
52
|
+
|
|
53
|
+
const builder = createBuilder<CreateCommandArgs>(createOptions, examples);
|
|
54
|
+
|
|
55
|
+
function findTemplatesRoot(): string {
|
|
56
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
57
|
+
const __dirname = dirname(__filename);
|
|
58
|
+
const candidates = [
|
|
59
|
+
// Bundled production binary (bin/ts-for-gir): ../dist-templates
|
|
60
|
+
resolve(__dirname, "..", "dist-templates"),
|
|
61
|
+
// Source layout (src/commands/create.ts): ../../dist-templates then ../../templates
|
|
62
|
+
resolve(__dirname, "..", "..", "dist-templates"),
|
|
63
|
+
resolve(__dirname, "..", "..", "templates"),
|
|
64
|
+
];
|
|
65
|
+
for (const path of candidates) {
|
|
66
|
+
if (existsSync(path)) return path;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Could not locate templates directory. Looked in:\n ${candidates.join("\n ")}\n` +
|
|
70
|
+
"If you are running from source, make sure packages/cli/templates/ exists. " +
|
|
71
|
+
"If you are running the published CLI, make sure dist-templates/ was packed.",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function listTemplates(templatesRoot: string): CreateTemplateId[] {
|
|
76
|
+
const knownIds = TEMPLATE_CHOICES.map((c) => c.value) as readonly string[];
|
|
77
|
+
return readdirSync(templatesRoot, { withFileTypes: true })
|
|
78
|
+
.filter((entry) => entry.isDirectory())
|
|
79
|
+
.map((entry) => entry.name)
|
|
80
|
+
.filter((name): name is CreateTemplateId => knownIds.includes(name));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sanitizeProjectName(raw: string): string {
|
|
84
|
+
const trimmed = raw.trim();
|
|
85
|
+
if (!trimmed) throw new Error("Project name cannot be empty");
|
|
86
|
+
// npm package name rules: lowercase letters, digits, dashes, underscores, dots; no leading dot/underscore
|
|
87
|
+
const cleaned = trimmed
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
90
|
+
.replace(/^[._-]+/, "")
|
|
91
|
+
.replace(/[._-]+$/, "");
|
|
92
|
+
if (!cleaned) throw new Error(`"${raw}" is not a valid npm package name`);
|
|
93
|
+
return cleaned;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isDirEmpty(path: string): boolean {
|
|
97
|
+
if (!existsSync(path)) return true;
|
|
98
|
+
return readdirSync(path).length === 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function substituteInFile(filePath: string, projectName: string): void {
|
|
102
|
+
const content = readFileSync(filePath, "utf8");
|
|
103
|
+
if (!content.includes(PROJECT_NAME_PLACEHOLDER)) return;
|
|
104
|
+
writeFileSync(filePath, content.replaceAll(PROJECT_NAME_PLACEHOLDER, projectName));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function walkAndSubstitute(rootDir: string, projectName: string): void {
|
|
108
|
+
for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
|
|
109
|
+
const full = join(rootDir, entry.name);
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
if (entry.name === "node_modules") continue;
|
|
112
|
+
walkAndSubstitute(full, projectName);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!entry.isFile()) continue;
|
|
116
|
+
const ext = entry.name.slice(entry.name.lastIndexOf("."));
|
|
117
|
+
if (!TEXT_FILE_EXT.has(ext)) continue;
|
|
118
|
+
substituteInFile(full, projectName);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handler = async (args: ConfigFlags) => {
|
|
123
|
+
const opts = args as unknown as CreateCommandArgs;
|
|
124
|
+
const log = new Logger(opts.verbose ?? false, "CreateCommand");
|
|
125
|
+
|
|
126
|
+
const templatesRoot = findTemplatesRoot();
|
|
127
|
+
const available = listTemplates(templatesRoot);
|
|
128
|
+
if (available.length === 0) {
|
|
129
|
+
throw new Error(`No templates found in ${templatesRoot}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let template: CreateTemplateId | undefined = opts.template;
|
|
133
|
+
if (template && !available.includes(template)) {
|
|
134
|
+
throw new Error(`Unknown template "${template}". Available: ${available.join(", ")}`);
|
|
135
|
+
}
|
|
136
|
+
if (!template) {
|
|
137
|
+
if (!process.stdin.isTTY) {
|
|
138
|
+
throw new Error(`--template is required (non-TTY). Available: ${available.join(", ")}`);
|
|
139
|
+
}
|
|
140
|
+
template = await select<CreateTemplateId>({
|
|
141
|
+
message: "Choose a template:",
|
|
142
|
+
choices: TEMPLATE_CHOICES.filter((c) => available.includes(c.value)).map((c) => ({
|
|
143
|
+
value: c.value,
|
|
144
|
+
name: c.name,
|
|
145
|
+
description: c.description,
|
|
146
|
+
})),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let nameRaw = opts.name;
|
|
151
|
+
if (!nameRaw) {
|
|
152
|
+
if (!process.stdin.isTTY) {
|
|
153
|
+
throw new Error("Project name is required (non-TTY). Pass it as the first positional argument.");
|
|
154
|
+
}
|
|
155
|
+
nameRaw = await input({
|
|
156
|
+
message: "Project name:",
|
|
157
|
+
default: "my-gjs-app",
|
|
158
|
+
validate: (v) => (v.trim().length > 0 ? true : "Project name cannot be empty"),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const projectName = sanitizeProjectName(nameRaw);
|
|
162
|
+
|
|
163
|
+
const targetDir = resolve(process.cwd(), projectName);
|
|
164
|
+
if (existsSync(targetDir) && !isDirEmpty(targetDir) && !opts.force) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Target directory ${targetDir} exists and is not empty. Use --force to scaffold into a non-empty directory.`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
mkdirSync(targetDir, { recursive: true });
|
|
170
|
+
|
|
171
|
+
const templateDir = join(templatesRoot, template);
|
|
172
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
173
|
+
walkAndSubstitute(targetDir, projectName);
|
|
174
|
+
|
|
175
|
+
log.success(`Scaffolded ${template} into ${targetDir}`);
|
|
176
|
+
|
|
177
|
+
if (opts.install) {
|
|
178
|
+
log.info("Running npm install...");
|
|
179
|
+
const result = spawnSync("npm", ["install", "--no-audit", "--no-fund"], {
|
|
180
|
+
cwd: targetDir,
|
|
181
|
+
stdio: "inherit",
|
|
182
|
+
});
|
|
183
|
+
if (result.status !== 0) {
|
|
184
|
+
log.warn("npm install failed; you can re-run it manually in the project directory.");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
log.info("\nNext steps:");
|
|
189
|
+
log.white(` cd ${projectName}`);
|
|
190
|
+
if (!opts.install) log.white(" npm install");
|
|
191
|
+
switch (template) {
|
|
192
|
+
case "types-locally":
|
|
193
|
+
log.white(" npm run generate");
|
|
194
|
+
log.white(" npm run check:types");
|
|
195
|
+
log.white(" npm run build && npm start");
|
|
196
|
+
break;
|
|
197
|
+
case "types-npm":
|
|
198
|
+
log.white(" npm run check");
|
|
199
|
+
log.white(" npm run build && npm start");
|
|
200
|
+
break;
|
|
201
|
+
case "types-workspace":
|
|
202
|
+
log.white(" npm run build:types && npm install");
|
|
203
|
+
log.white(" npm run build:app && npm start");
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const create = {
|
|
209
|
+
command,
|
|
210
|
+
description,
|
|
211
|
+
builder,
|
|
212
|
+
handler,
|
|
213
|
+
examples,
|
|
214
|
+
};
|
package/src/commands/index.ts
CHANGED
|
@@ -141,6 +141,7 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
141
141
|
const stringKeys: Array<[keyof UserConfig, unknown]> = [
|
|
142
142
|
["npmScope", options.npmScope.default],
|
|
143
143
|
["reporterOutput", options.reporterOutput.default],
|
|
144
|
+
["depVersionFormat", undefined],
|
|
144
145
|
["theme", docOptions.theme.default],
|
|
145
146
|
["sourceLinkTemplate", undefined],
|
|
146
147
|
["readme", undefined],
|
package/src/config/index.ts
CHANGED
|
@@ -5,4 +5,12 @@
|
|
|
5
5
|
export { getOptionsGeneration, load, validate } from "./config-loader.ts";
|
|
6
6
|
export { addToConfig, configFilePath } from "./config-writer.ts";
|
|
7
7
|
export { defaults } from "./defaults.ts";
|
|
8
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
analyzeOptions,
|
|
10
|
+
copyOptions,
|
|
11
|
+
createOptions,
|
|
12
|
+
docOptions,
|
|
13
|
+
generateOptions,
|
|
14
|
+
listOptions,
|
|
15
|
+
options,
|
|
16
|
+
} from "./options.ts";
|