@vlandoss/starter 0.0.1
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 +34 -0
- package/bin.ts +6 -0
- package/package.json +41 -0
- package/plopfiles/plopfile.ts +84 -0
- package/plopfiles/plugins/changeset/.changeset/README.md.hbs +8 -0
- package/plopfiles/plugins/changeset/.changeset/config.json.hbs +11 -0
- package/plopfiles/plugins/husky/.husky/pre-commit +2 -0
- package/plopfiles/plugins/husky/.husky/pre-push +1 -0
- package/plopfiles/plugins/mise/mise.toml.hbs +2 -0
- package/plopfiles/plugins/tsup/tsup.config.ts.hbs +10 -0
- package/plopfiles/templates/#common/.gitignore.hbs +180 -0
- package/plopfiles/templates/#common/README.md.hbs +3 -0
- package/plopfiles/templates/#common/biome.json.hbs +3 -0
- package/plopfiles/templates/basic/package.json.hbs +32 -0
- package/plopfiles/templates/basic/src/index.ts.hbs +1 -0
- package/plopfiles/templates/monorepo/package.json.hbs +27 -0
- package/plopfiles/templates/monorepo/pnpm-workspace.yaml.hbs +4 -0
- package/src/actions/add.ts +48 -0
- package/src/actions/init.ts +69 -0
- package/src/actions/types.ts +3 -0
- package/src/main.ts +12 -0
- package/src/program/commands/add.ts +40 -0
- package/src/program/commands/init.ts +47 -0
- package/src/program/index.ts +20 -0
- package/src/program/ui.ts +7 -0
- package/src/services/config.ts +28 -0
- package/src/services/ctx.ts +38 -0
- package/src/services/logger.ts +5 -0
- package/src/services/template.ts +64 -0
- package/src/services/types.ts +12 -0
- package/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ⚡ vland
|
|
2
|
+
|
|
3
|
+
The CLI to init a new project in Variable Land 👊
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Bun >= 1.2.4
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add -g @vlandoss/starter
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
It will adds the `vland` to your global workspace
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
> [!NOTE]
|
|
20
|
+
> The documentation is WIP
|
|
21
|
+
|
|
22
|
+
Run the help command:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
vland help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Troubleshooting
|
|
29
|
+
|
|
30
|
+
To enable debug mode, set the `DEBUG` environment variable to `vland:*` before running *any* command.
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
DEBUG=vland:* vland help
|
|
34
|
+
```
|
package/bin.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vlandoss/starter",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "The CLI to init a new project in Variable Land",
|
|
5
|
+
"homepage": "https://github.com/variableland/dx/tree/main/packages/starter#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/variableland/dx/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/variableland/dx.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"module": "src/main.ts",
|
|
17
|
+
"bin": {
|
|
18
|
+
"vland": "./bin.ts"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"plopfiles",
|
|
24
|
+
"tsconfig.json"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "13.1.0",
|
|
28
|
+
"node-plop": "0.32.0",
|
|
29
|
+
"@vlandoss/clibuddy": "0.0.1",
|
|
30
|
+
"@vlandoss/loggy": "0.0.1"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"bun": ">=1.0.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"typecheck": "rr tsc"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { NodePlopAPI } from "node-plop";
|
|
3
|
+
import { ConfigService } from "~/services/config";
|
|
4
|
+
|
|
5
|
+
export default function configPlop(plop: NodePlopAPI) {
|
|
6
|
+
const baseDir = path.dirname(plop.getPlopfilePath());
|
|
7
|
+
const configService = new ConfigService(baseDir);
|
|
8
|
+
|
|
9
|
+
function atLeastOne(answer: string[]) {
|
|
10
|
+
if (answer.length === 0) {
|
|
11
|
+
return "At least one option must be selected";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
plop.setGenerator("init", {
|
|
18
|
+
description: "Initialize a project based on a predefined template",
|
|
19
|
+
prompts: [
|
|
20
|
+
{
|
|
21
|
+
type: "list",
|
|
22
|
+
name: "template",
|
|
23
|
+
message: "Template:",
|
|
24
|
+
choices: configService.getTemplateChoices(),
|
|
25
|
+
validate: atLeastOne,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "input",
|
|
29
|
+
name: "name",
|
|
30
|
+
message: "Name:",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: "input",
|
|
34
|
+
name: "description",
|
|
35
|
+
message: "Description:",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
actions: [
|
|
39
|
+
{
|
|
40
|
+
type: "addMany",
|
|
41
|
+
destination: ".",
|
|
42
|
+
base: "templates/#common",
|
|
43
|
+
templateFiles: ["templates/#common/**"],
|
|
44
|
+
globOptions: {
|
|
45
|
+
dot: true,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: "addMany",
|
|
50
|
+
destination: ".",
|
|
51
|
+
base: "templates/{{template}}",
|
|
52
|
+
templateFiles: ["templates/{{template}}/**"],
|
|
53
|
+
globOptions: {
|
|
54
|
+
dot: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
plop.setGenerator("add", {
|
|
61
|
+
description: "Add config file(s) to a project",
|
|
62
|
+
prompts: [
|
|
63
|
+
{
|
|
64
|
+
type: "checkbox",
|
|
65
|
+
name: "slugs",
|
|
66
|
+
message: "Select configs:",
|
|
67
|
+
choices: configService.getPluginChoices(),
|
|
68
|
+
validate: atLeastOne,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
actions: (answers: unknown) => {
|
|
72
|
+
// @ts-expect-error
|
|
73
|
+
return answers.slugs.map((slug) => ({
|
|
74
|
+
type: "addMany",
|
|
75
|
+
destination: ".",
|
|
76
|
+
base: `plugins/${slug}`,
|
|
77
|
+
templateFiles: [`plugins/${slug}/**`],
|
|
78
|
+
globOptions: {
|
|
79
|
+
dot: true,
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changesets
|
|
2
|
+
|
|
3
|
+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
4
|
+
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
5
|
+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
6
|
+
|
|
7
|
+
We have a quick list of common questions to get you started engaging with this project in
|
|
8
|
+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
|
|
3
|
+
"changelog": ["@changesets/changelog-github", { "repo": "variableland/{{name}}" }],
|
|
4
|
+
"commit": false,
|
|
5
|
+
"fixed": [],
|
|
6
|
+
"linked": [],
|
|
7
|
+
"access": "restricted",
|
|
8
|
+
"baseBranch": "main",
|
|
9
|
+
"updateInternalDependencies": "patch",
|
|
10
|
+
"ignore": []
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pnpm test
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
|
2
|
+
|
|
3
|
+
# Logs
|
|
4
|
+
|
|
5
|
+
logs
|
|
6
|
+
_.log
|
|
7
|
+
npm-debug.log_
|
|
8
|
+
yarn-debug.log*
|
|
9
|
+
yarn-error.log*
|
|
10
|
+
lerna-debug.log*
|
|
11
|
+
.pnpm-debug.log*
|
|
12
|
+
|
|
13
|
+
# Caches
|
|
14
|
+
|
|
15
|
+
.cache
|
|
16
|
+
.turbo
|
|
17
|
+
|
|
18
|
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
19
|
+
|
|
20
|
+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
21
|
+
|
|
22
|
+
# Runtime data
|
|
23
|
+
|
|
24
|
+
pids
|
|
25
|
+
_.pid
|
|
26
|
+
_.seed
|
|
27
|
+
*.pid.lock
|
|
28
|
+
|
|
29
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
30
|
+
|
|
31
|
+
lib-cov
|
|
32
|
+
|
|
33
|
+
# Coverage directory used by tools like istanbul
|
|
34
|
+
|
|
35
|
+
coverage
|
|
36
|
+
*.lcov
|
|
37
|
+
|
|
38
|
+
# nyc test coverage
|
|
39
|
+
|
|
40
|
+
.nyc_output
|
|
41
|
+
|
|
42
|
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
43
|
+
|
|
44
|
+
.grunt
|
|
45
|
+
|
|
46
|
+
# Bower dependency directory (https://bower.io/)
|
|
47
|
+
|
|
48
|
+
bower_components
|
|
49
|
+
|
|
50
|
+
# node-waf configuration
|
|
51
|
+
|
|
52
|
+
.lock-wscript
|
|
53
|
+
|
|
54
|
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
55
|
+
|
|
56
|
+
build/Release
|
|
57
|
+
|
|
58
|
+
# Dependency directories
|
|
59
|
+
|
|
60
|
+
node_modules/
|
|
61
|
+
jspm_packages/
|
|
62
|
+
|
|
63
|
+
# Snowpack dependency directory (https://snowpack.dev/)
|
|
64
|
+
|
|
65
|
+
web_modules/
|
|
66
|
+
|
|
67
|
+
# TypeScript cache
|
|
68
|
+
|
|
69
|
+
*.tsbuildinfo
|
|
70
|
+
|
|
71
|
+
# Optional npm cache directory
|
|
72
|
+
|
|
73
|
+
.npm
|
|
74
|
+
|
|
75
|
+
# Optional eslint cache
|
|
76
|
+
|
|
77
|
+
.eslintcache
|
|
78
|
+
|
|
79
|
+
# Optional stylelint cache
|
|
80
|
+
|
|
81
|
+
.stylelintcache
|
|
82
|
+
|
|
83
|
+
# Microbundle cache
|
|
84
|
+
|
|
85
|
+
.rpt2_cache/
|
|
86
|
+
.rts2_cache_cjs/
|
|
87
|
+
.rts2_cache_es/
|
|
88
|
+
.rts2_cache_umd/
|
|
89
|
+
|
|
90
|
+
# Optional REPL history
|
|
91
|
+
|
|
92
|
+
.node_repl_history
|
|
93
|
+
|
|
94
|
+
# Output of 'npm pack'
|
|
95
|
+
|
|
96
|
+
*.tgz
|
|
97
|
+
|
|
98
|
+
# Yarn Integrity file
|
|
99
|
+
|
|
100
|
+
.yarn-integrity
|
|
101
|
+
|
|
102
|
+
# dotenv environment variable files
|
|
103
|
+
|
|
104
|
+
.env
|
|
105
|
+
.env.development.local
|
|
106
|
+
.env.test.local
|
|
107
|
+
.env.production.local
|
|
108
|
+
.env.local
|
|
109
|
+
|
|
110
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
111
|
+
|
|
112
|
+
.parcel-cache
|
|
113
|
+
|
|
114
|
+
# Next.js build output
|
|
115
|
+
|
|
116
|
+
.next
|
|
117
|
+
out
|
|
118
|
+
|
|
119
|
+
# Nuxt.js build / generate output
|
|
120
|
+
|
|
121
|
+
.nuxt
|
|
122
|
+
dist
|
|
123
|
+
|
|
124
|
+
# Gatsby files
|
|
125
|
+
|
|
126
|
+
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
127
|
+
|
|
128
|
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
129
|
+
|
|
130
|
+
# public
|
|
131
|
+
|
|
132
|
+
# vuepress build output
|
|
133
|
+
|
|
134
|
+
.vuepress/dist
|
|
135
|
+
|
|
136
|
+
# vuepress v2.x temp and cache directory
|
|
137
|
+
|
|
138
|
+
.temp
|
|
139
|
+
|
|
140
|
+
# Docusaurus cache and generated files
|
|
141
|
+
|
|
142
|
+
.docusaurus
|
|
143
|
+
|
|
144
|
+
# Serverless directories
|
|
145
|
+
|
|
146
|
+
.serverless/
|
|
147
|
+
|
|
148
|
+
# FuseBox cache
|
|
149
|
+
|
|
150
|
+
.fusebox/
|
|
151
|
+
|
|
152
|
+
# DynamoDB Local files
|
|
153
|
+
|
|
154
|
+
.dynamodb/
|
|
155
|
+
|
|
156
|
+
# TernJS port file
|
|
157
|
+
|
|
158
|
+
.tern-port
|
|
159
|
+
|
|
160
|
+
# Stores VSCode versions used for testing VSCode extensions
|
|
161
|
+
|
|
162
|
+
.vscode-test
|
|
163
|
+
|
|
164
|
+
# yarn v2
|
|
165
|
+
|
|
166
|
+
.yarn/cache
|
|
167
|
+
.yarn/unplugged
|
|
168
|
+
.yarn/build-state.yml
|
|
169
|
+
.yarn/install-state.gz
|
|
170
|
+
.pnp.*
|
|
171
|
+
|
|
172
|
+
# IntelliJ based IDEs
|
|
173
|
+
.idea
|
|
174
|
+
|
|
175
|
+
# Finder (MacOS) folder config
|
|
176
|
+
.DS_Store
|
|
177
|
+
|
|
178
|
+
# Bun
|
|
179
|
+
|
|
180
|
+
*.bun-build
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vlandoss/{{name}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"homepage": "https://github.com/variableland/{{name}}#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/variableland/{{name}}/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/variableland/{{name}}.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@total-typescript/tsconfig": "1.0.4",
|
|
19
|
+
"@types/bun": "1.2.9",
|
|
20
|
+
"@vlandoss/biome-config": "latest",
|
|
21
|
+
"@vlandoss/run-run": "latest"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"bun": ">=1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"packageManager": "pnpm@10.6.4",
|
|
27
|
+
"pnpm": {
|
|
28
|
+
"onlyBuiltDependencies": [
|
|
29
|
+
"@biomejs/biome"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("Hi, from {{name}} 👋");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vlandoss/{{name}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"homepage": "https://github.com/variableland/{{name}}#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/variableland/{{name}}/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/variableland/{{name}}.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@total-typescript/tsconfig": "1.0.4",
|
|
19
|
+
"@types/bun": "1.2.9",
|
|
20
|
+
"@vlandoss/biome-config": "latest",
|
|
21
|
+
"@vlandoss/run-run": "latest"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"bun": ">=1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"packageManager": "pnpm@10.6.4"
|
|
27
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { logger } from "~/services/logger";
|
|
2
|
+
import type { TemplateService } from "~/services/types";
|
|
3
|
+
import type { AnyAction } from "./types";
|
|
4
|
+
|
|
5
|
+
type CreateOptions = {
|
|
6
|
+
templateService: TemplateService;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ExecuteOptions = {
|
|
10
|
+
slugs: string[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const GENERATOR_ID = "add";
|
|
14
|
+
|
|
15
|
+
export class AddAction implements AnyAction<ExecuteOptions> {
|
|
16
|
+
#templateService: TemplateService;
|
|
17
|
+
|
|
18
|
+
constructor({ templateService }: CreateOptions) {
|
|
19
|
+
this.#templateService = templateService;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async execute(options: ExecuteOptions) {
|
|
23
|
+
const debug = logger.subdebug("add-action");
|
|
24
|
+
|
|
25
|
+
debug("execute options: %O", options);
|
|
26
|
+
|
|
27
|
+
const bypassArr = this.#getBypassArr(options);
|
|
28
|
+
|
|
29
|
+
await this.#templateService.generate({
|
|
30
|
+
bypassArr,
|
|
31
|
+
generatorId: GENERATOR_ID,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
logger.success("Added successfully 🎉");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#getBypassArr(options: ExecuteOptions) {
|
|
38
|
+
const { slugs } = options;
|
|
39
|
+
|
|
40
|
+
const bypassArr: string[] = [];
|
|
41
|
+
|
|
42
|
+
if (slugs.length) {
|
|
43
|
+
bypassArr[0] = slugs.join(",");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return bypassArr;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import { logger } from "~/services/logger";
|
|
3
|
+
import type { TemplateService } from "~/services/types";
|
|
4
|
+
import type { AnyAction } from "./types";
|
|
5
|
+
|
|
6
|
+
type ExecuteOptions = {
|
|
7
|
+
template?: string;
|
|
8
|
+
git: boolean;
|
|
9
|
+
destBasePath: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type CreateOptions = {
|
|
13
|
+
templateService: TemplateService;
|
|
14
|
+
shellService: ShellService;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const GENERATOR_ID = "init";
|
|
18
|
+
|
|
19
|
+
export class InitAction implements AnyAction<ExecuteOptions> {
|
|
20
|
+
#templateService: TemplateService;
|
|
21
|
+
#shellService: ShellService;
|
|
22
|
+
|
|
23
|
+
constructor({ templateService, shellService }: CreateOptions) {
|
|
24
|
+
this.#templateService = templateService;
|
|
25
|
+
this.#shellService = shellService;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async execute(options: ExecuteOptions) {
|
|
29
|
+
const { destBasePath, git } = options;
|
|
30
|
+
|
|
31
|
+
const debug = logger.subdebug("init-action");
|
|
32
|
+
|
|
33
|
+
debug("execute options: %O", options);
|
|
34
|
+
|
|
35
|
+
const bypassArr = this.#getBypassArr(options);
|
|
36
|
+
|
|
37
|
+
await this.#templateService.generate({
|
|
38
|
+
bypassArr,
|
|
39
|
+
generatorId: GENERATOR_ID,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
logger.success("Project generated 🎉");
|
|
43
|
+
|
|
44
|
+
const $ = this.#shellService.$;
|
|
45
|
+
const $$ = $.quiet({ cwd: destBasePath });
|
|
46
|
+
|
|
47
|
+
if (git) {
|
|
48
|
+
logger.start("Creating git repository");
|
|
49
|
+
|
|
50
|
+
await $$`git init`;
|
|
51
|
+
// NOTE: git commit -am failed, not sure why
|
|
52
|
+
await $$`git add . && git commit -m "initial commit"`;
|
|
53
|
+
|
|
54
|
+
logger.success("Git repository created");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#getBypassArr(options: ExecuteOptions) {
|
|
59
|
+
const { template } = options;
|
|
60
|
+
|
|
61
|
+
const bypassArr: string[] = [];
|
|
62
|
+
|
|
63
|
+
if (template) {
|
|
64
|
+
bypassArr[0] = template;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return bypassArr;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Options, createProgram } from "./program";
|
|
2
|
+
import { logger } from "./services/logger";
|
|
3
|
+
|
|
4
|
+
export async function main(options: Options) {
|
|
5
|
+
try {
|
|
6
|
+
const program = await createProgram(options);
|
|
7
|
+
await program.parseAsync();
|
|
8
|
+
} catch (error) {
|
|
9
|
+
logger.error("Cannot run main successfully", error);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cwd } from "@vlandoss/clibuddy";
|
|
2
|
+
import { Argument, Option, createCommand } from "commander";
|
|
3
|
+
import { AddAction } from "~/actions/add";
|
|
4
|
+
import type { ContextValue } from "~/services/ctx";
|
|
5
|
+
import { logger } from "~/services/logger";
|
|
6
|
+
import { createPlopTemplateService } from "~/services/template";
|
|
7
|
+
|
|
8
|
+
type AddOptions = {
|
|
9
|
+
dest: string;
|
|
10
|
+
force: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function createAddCommand(ctx: ContextValue) {
|
|
14
|
+
return createCommand("add")
|
|
15
|
+
.description("add config files to a project 📁")
|
|
16
|
+
.addArgument(new Argument("[slug...]", "the config slugs to pick").choices(ctx.config.getPluginChoices()))
|
|
17
|
+
.addOption(new Option("-d, --dest <string>", "destination path to create folder (default: cwd)"))
|
|
18
|
+
.addOption(new Option("-f, --force", "override existing files").default(false))
|
|
19
|
+
.action(async function addAction(slugs: string[], options: AddOptions) {
|
|
20
|
+
try {
|
|
21
|
+
const { dest: destBasePath = cwd, force } = options;
|
|
22
|
+
|
|
23
|
+
const templateService = await createPlopTemplateService({
|
|
24
|
+
force,
|
|
25
|
+
destBasePath,
|
|
26
|
+
basePath: ctx.binPkg.dirPath,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const addAction = new AddAction({
|
|
30
|
+
templateService,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await addAction.execute({ slugs });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.error(error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses Plop.js to generate the project.");
|
|
40
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cwd } from "@vlandoss/clibuddy";
|
|
2
|
+
import { Argument, Option, createCommand } from "commander";
|
|
3
|
+
import { InitAction } from "~/actions/init";
|
|
4
|
+
import type { ContextValue } from "~/services/ctx";
|
|
5
|
+
import { logger } from "~/services/logger";
|
|
6
|
+
import { createPlopTemplateService } from "~/services/template";
|
|
7
|
+
|
|
8
|
+
type InitOptions = {
|
|
9
|
+
dest: string;
|
|
10
|
+
git: boolean;
|
|
11
|
+
force: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function createInitCommand(ctx: ContextValue) {
|
|
15
|
+
return createCommand("init")
|
|
16
|
+
.description("init a new project 🚀")
|
|
17
|
+
.addArgument(new Argument("[template]", "the template to use").choices(ctx.config.getTemplateChoices()))
|
|
18
|
+
.addOption(new Option("-d, --dest [string]", "destination path to create folder (default: cwd)"))
|
|
19
|
+
.addOption(new Option("--no-git", "skip to create a git repository").default(true))
|
|
20
|
+
.addOption(new Option("-f, --force", "override existing files").default(false))
|
|
21
|
+
.action(async function initAction(template: string | undefined, options: InitOptions) {
|
|
22
|
+
try {
|
|
23
|
+
const { dest: destBasePath = cwd, force } = options;
|
|
24
|
+
|
|
25
|
+
const templateService = await createPlopTemplateService({
|
|
26
|
+
force,
|
|
27
|
+
destBasePath,
|
|
28
|
+
basePath: ctx.binPkg.dirPath,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const initAction = new InitAction({
|
|
32
|
+
templateService,
|
|
33
|
+
shellService: ctx.shell,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await initAction.execute({
|
|
37
|
+
template,
|
|
38
|
+
destBasePath,
|
|
39
|
+
...options,
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.error(error);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses Plop.js to generate the project.");
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getVersion } from "@vlandoss/clibuddy";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createContext } from "~/services/ctx";
|
|
4
|
+
import { createAddCommand } from "./commands/add";
|
|
5
|
+
import { createInitCommand } from "./commands/init";
|
|
6
|
+
import { BANNER_TEXT } from "./ui";
|
|
7
|
+
|
|
8
|
+
export type Options = {
|
|
9
|
+
binDir: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function createProgram(options: Options) {
|
|
13
|
+
const ctx = await createContext(options.binDir);
|
|
14
|
+
|
|
15
|
+
return new Command("vland")
|
|
16
|
+
.version(getVersion(ctx.binPkg), "-v, --version")
|
|
17
|
+
.addHelpText("before", BANNER_TEXT)
|
|
18
|
+
.addCommand(createInitCommand(ctx))
|
|
19
|
+
.addCommand(createAddCommand(ctx));
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { colors } from "@vlandoss/clibuddy";
|
|
2
|
+
|
|
3
|
+
const UI_LOGO = `⚡ ${colors.blueBright("V")} ${colors.redBright("L")} ${colors.greenBright("A")} ${colors.blueBright("N")} ${colors.redBright("D")}`;
|
|
4
|
+
|
|
5
|
+
const COMPANY_LOGO = `${colors.redBright("Variable Land")} 👊`;
|
|
6
|
+
|
|
7
|
+
export const BANNER_TEXT = `${UI_LOGO}: The CLI to init a new project in ${COMPANY_LOGO}\n`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export class ConfigService {
|
|
5
|
+
#baseDir: string;
|
|
6
|
+
|
|
7
|
+
constructor(baseDir = "") {
|
|
8
|
+
this.#baseDir = baseDir;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getPluginChoices() {
|
|
12
|
+
const folderPath = this.#getPlopFolderDir("plugins");
|
|
13
|
+
return fs.readdirSync(folderPath).sort();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getTemplateChoices() {
|
|
17
|
+
const folderPath = this.#getPlopFolderDir("templates");
|
|
18
|
+
|
|
19
|
+
return fs
|
|
20
|
+
.readdirSync(folderPath)
|
|
21
|
+
.filter((folder) => !folder.startsWith("#"))
|
|
22
|
+
.sort();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#getPlopFolderDir(folder: string) {
|
|
26
|
+
return join(this.#baseDir, join("plopfiles", folder));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { type PkgService, type ShellService, createPkgService, createShellService } from "@vlandoss/clibuddy";
|
|
3
|
+
import { ConfigService } from "./config";
|
|
4
|
+
import { logger } from "./logger";
|
|
5
|
+
|
|
6
|
+
export type ContextValue = {
|
|
7
|
+
binPkg: PkgService;
|
|
8
|
+
config: ConfigService;
|
|
9
|
+
shell: ShellService;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function createContext(binDir: string): Promise<ContextValue> {
|
|
13
|
+
const debug = logger.subdebug("create-context-value");
|
|
14
|
+
|
|
15
|
+
const binPath = fs.realpathSync(binDir);
|
|
16
|
+
|
|
17
|
+
debug("bin path %s", binPath);
|
|
18
|
+
|
|
19
|
+
const binPkg = await createPkgService(binPath);
|
|
20
|
+
|
|
21
|
+
if (!binPkg) {
|
|
22
|
+
throw new Error("Could not find bin package.json");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
debug("bin pkg info %O", binPkg.info());
|
|
26
|
+
|
|
27
|
+
const config = new ConfigService(binDir);
|
|
28
|
+
|
|
29
|
+
const shell = createShellService({
|
|
30
|
+
localBaseBinPath: [binDir],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
binPkg,
|
|
35
|
+
config,
|
|
36
|
+
shell,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import type { NodePlopAPI } from "node-plop";
|
|
3
|
+
import nodePlop from "node-plop";
|
|
4
|
+
import { logger } from "./logger";
|
|
5
|
+
import type { GenerateOptions, TemplateService } from "./types";
|
|
6
|
+
|
|
7
|
+
type CreateOptions = {
|
|
8
|
+
basePath: string;
|
|
9
|
+
destBasePath: string;
|
|
10
|
+
force: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class PlopTemplateService implements TemplateService {
|
|
14
|
+
#plop: NodePlopAPI;
|
|
15
|
+
|
|
16
|
+
constructor(plop: NodePlopAPI) {
|
|
17
|
+
this.#plop = plop;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async generate(options: GenerateOptions) {
|
|
21
|
+
const { generatorId, bypassArr } = options;
|
|
22
|
+
|
|
23
|
+
const debug = logger.subdebug("plop-template-service:generate");
|
|
24
|
+
|
|
25
|
+
debug("generate options: %O", options);
|
|
26
|
+
|
|
27
|
+
const generator = this.#plop.getGenerator(generatorId);
|
|
28
|
+
|
|
29
|
+
const answers = await generator.runPrompts(bypassArr);
|
|
30
|
+
|
|
31
|
+
debug("generator answers: %O", answers);
|
|
32
|
+
|
|
33
|
+
const results = await generator.runActions(answers);
|
|
34
|
+
|
|
35
|
+
debug("generator results: %O", results);
|
|
36
|
+
|
|
37
|
+
if (results.failures.length > 0) {
|
|
38
|
+
throw new Error("Can't generate files");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { answers };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const PLOP_CONFIG_PATH = join("plopfiles", "plopfile.ts");
|
|
46
|
+
|
|
47
|
+
export async function createPlopTemplateService(options: CreateOptions) {
|
|
48
|
+
const { force, destBasePath } = options;
|
|
49
|
+
|
|
50
|
+
const debug = logger.subdebug("create-plop-template-service");
|
|
51
|
+
|
|
52
|
+
debug("options: %O", options);
|
|
53
|
+
|
|
54
|
+
const configPath = join(options.basePath, PLOP_CONFIG_PATH);
|
|
55
|
+
|
|
56
|
+
debug("plop config path:", configPath);
|
|
57
|
+
|
|
58
|
+
const plop = await nodePlop(configPath, {
|
|
59
|
+
force,
|
|
60
|
+
destBasePath,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return new PlopTemplateService(plop);
|
|
64
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type GenerateOptions = {
|
|
2
|
+
generatorId: string;
|
|
3
|
+
bypassArr?: string[];
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type GenerateResult<T> = {
|
|
7
|
+
answers: T;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type TemplateService = {
|
|
11
|
+
generate: <R>(options: GenerateOptions) => Promise<GenerateResult<R>>;
|
|
12
|
+
};
|