@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 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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bun
2
+ import { main } from "./src/main";
3
+
4
+ main({
5
+ binDir: __dirname,
6
+ });
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,2 @@
1
+ rr test:static --fix-staged
2
+ git update-index --again
@@ -0,0 +1 @@
1
+ pnpm test
@@ -0,0 +1,2 @@
1
+ [env]
2
+ _.path = ["\{{config_root}}/node_modules/.bin"]
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["esm", "cjs"],
6
+ splitting: false,
7
+ sourcemap: true,
8
+ clean: true,
9
+ dts: true,
10
+ });
@@ -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,3 @@
1
+ # {{titleCase name}}
2
+
3
+ {{description}}
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": ["@vlandoss/biome-config"]
3
+ }
@@ -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,4 @@
1
+ packages:
2
+ - packages/*
3
+ onlyBuiltDependencies:
4
+ - "@biomejs/biome"
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export type AnyAction<T> = {
2
+ execute: (options: T) => Promise<void>;
3
+ };
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,5 @@
1
+ import { createLoggy } from "@vlandoss/loggy";
2
+
3
+ export const logger = createLoggy({
4
+ namespace: "vland",
5
+ });
@@ -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
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": ["@total-typescript/tsconfig/bundler/no-dom/app"],
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "~/*": ["./src/*"],
7
+ "@vlandoss/*": ["../../packages/*/src"]
8
+ }
9
+ }
10
+ }