@codeandmoney/jargal 0.0.0-rc.1 → 0.0.0-rc.4

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.
@@ -0,0 +1,7 @@
1
+ // import type { Action } from "../types.ts";
2
+
3
+ // export function pipe(): Action {
4
+ // return function execute(_params) {
5
+ // throw new Error("Action is not implemented");
6
+ // };
7
+ // }
@@ -1,15 +1,15 @@
1
1
  import type { Action, Context } from "../types.ts";
2
2
 
3
3
  export function renderTemplate({
4
- fullpath,
4
+ templatePath,
5
5
  template,
6
6
  getData,
7
- write,
7
+ save,
8
8
  }: {
9
9
  template: string;
10
- fullpath: string;
10
+ templatePath: string;
11
11
  getData?: (ctx: Context) => Record<string, unknown>;
12
- write?: Action<{ content?: string; destination?: string }>;
12
+ save?: Action<{ templateContent?: string; destination?: string }>;
13
13
  }): Action {
14
14
  if (!template) {
15
15
  return () => undefined;
@@ -20,17 +20,10 @@ export function renderTemplate({
20
20
 
21
21
  const renderedTemplate = params.renderer.renderString({ template, data });
22
22
 
23
- const renderedPath = params.renderer.renderString({
24
- template: fullpath,
25
- data,
26
- });
23
+ const renderedPath = params.renderer.renderString({ template: templatePath, data });
27
24
 
28
- if (write) {
29
- return write({
30
- ...params,
31
- content: renderedTemplate,
32
- destination: renderedPath,
33
- });
25
+ if (save) {
26
+ return save({ ...params, context: { ...params.context, templateContent: renderedTemplate, destination: renderedPath } });
34
27
  }
35
28
  };
36
29
  }
@@ -0,0 +1,20 @@
1
+ import type { BaseIssue, BaseAction, Config, InferIssue, InferOutput } from "../../types/index.ts";
2
+
3
+ /**
4
+ * Parses an unknown input based on a schema.
5
+ *
6
+ * @param action The action to be used.
7
+ * @param input The input to be parsed.
8
+ * @param config The run configuration.
9
+ *
10
+ * @returns The parsed input.
11
+ */
12
+ export function run<const Action extends BaseAction<unknown, unknown>>(action: Action, input: unknown): InferOutput<Action> {
13
+ const context = action["~run"]({ value: input }, {});
14
+
15
+ if (context.issues) {
16
+ throw new Error("msg");
17
+ }
18
+
19
+ return context.value;
20
+ }
@@ -6,7 +6,7 @@ const primitiveSchema = v.union([v.pipe(v.string(), v.minLength(1)), v.boolean()
6
6
 
7
7
  const defaultSchema = v.record(v.string(), v.union([primitiveSchema, v.array(primitiveSchema)]));
8
8
 
9
- export function validateAnswers<Schema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema?: Schema): Action {
9
+ export function validateAnswers<Schema extends v.BaseAction<unknown, unknown, v.BaseIssue<unknown>>>(schema?: Schema): Action {
10
10
  return function execute(params) {
11
11
  v.assert(schema ?? defaultSchema, params.context.answers);
12
12
  };
@@ -0,0 +1,69 @@
1
+ import type { BaseIssue, BaseAction, ErrorMessage, Context, OutputContext, BaseContext } from "../../types/index.ts";
2
+ // import { _addIssue, _getStandardProps } from "../../utils/index.ts";
3
+
4
+ /**
5
+ * Write issue interface.
6
+ */
7
+ export interface WriteIssue extends BaseIssue<unknown> {
8
+ /**
9
+ * The issue kind.
10
+ */
11
+ readonly kind: "action";
12
+ /**
13
+ * The issue type.
14
+ */
15
+ readonly type: "string";
16
+ }
17
+
18
+ /**
19
+ * Write schema interface.
20
+ */
21
+ export interface WriteAction<Config extends Record<string, any> | undefined, Output extends Record<string, any>> extends BaseAction<Config, Output> {
22
+ readonly config: Config;
23
+
24
+ /**
25
+ * The expected property.
26
+ */
27
+ readonly expects: "string";
28
+ }
29
+
30
+ export type WriteActionConfig = {
31
+ destination?: string;
32
+ templateContent?: string;
33
+ mode?: "force" | "skip-if-exists";
34
+ };
35
+
36
+ // /**
37
+ // * Creates a string schema.
38
+ // *
39
+ // * @param config The error config.
40
+ // *
41
+ // * @returns A string schema.
42
+ // */
43
+
44
+ export function write<Ctx extends Record<string, any>>(config?: WriteActionConfig): WriteAction<WriteActionConfig, Ctx>;
45
+ export function write<Ctx extends undefined = undefined>(config: WriteActionConfig): WriteAction<WriteActionConfig, {}>;
46
+ export function write<Ctx extends undefined | Record<string, any> = undefined>(
47
+ config?: WriteActionConfig,
48
+ ): WriteAction<WriteActionConfig, Ctx extends undefined ? {} : Ctx> {
49
+ return {
50
+ kind: "action",
51
+ expects: "string",
52
+
53
+ config: config ?? {},
54
+ "~run"(context, config) {
55
+ context.renderer;
56
+
57
+ if (typeof context.value === "string") {
58
+ // @ts-expect-error
59
+ context.typed = true;
60
+ } else {
61
+ // _addIssue(this, "type", context, config);
62
+ }
63
+ // @ts-expect-error
64
+ return context as OutputContext<string, WriteIssue>;
65
+ },
66
+ };
67
+ }
68
+
69
+ // hooks?: ActionHooks;
package/actions/write.ts CHANGED
@@ -6,38 +6,42 @@ import { dirname } from "node:path";
6
6
 
7
7
  export type WriteActionConfig = {
8
8
  destination?: string;
9
- content?: string;
9
+ templateContent?: string;
10
10
  mode?: "force" | "skip-if-exists";
11
11
  };
12
12
 
13
- export function write({ destination, content, mode }: WriteActionConfig): Action<{ content?: string; destination?: string }> {
14
- return async function execute({ content: content_, destination: destination_ }) {
15
- const dest = destination || destination_;
16
- const cntn = content || content_;
13
+ export function write({
14
+ destination: writeDestination,
15
+ templateContent: writeContent,
16
+ mode,
17
+ }: WriteActionConfig): Action<{ templateContent?: string; destination?: string }> {
18
+ return async function execute({ context: { templateContent: executeContent, destination: executeDestination } }) {
19
+ const destination = executeDestination || writeDestination;
20
+ const templateContent = executeContent || writeContent;
17
21
 
18
- assert(dest, "must provide `dest`");
19
- assert(cntn, "must provide `cntn`");
22
+ assert(destination, "must provide `destination`");
23
+ assert(templateContent, "must provide `templateContent`");
20
24
 
21
- await mkdir(dirname(dest), { recursive: true });
25
+ await mkdir(dirname(destination), { recursive: true });
22
26
 
23
- let doesExist = await fileExists(dest);
27
+ let doesExist = await fileExists(destination);
24
28
 
25
29
  if (doesExist && mode === "force") {
26
- await rm(dest, { recursive: true });
30
+ await rm(destination, { recursive: true, force: true });
27
31
  doesExist = false;
28
32
  }
29
33
 
30
34
  if (doesExist && mode !== "skip-if-exists") {
31
- throw `File already exists\n -> ${dest}`;
35
+ throw `File already exists\n -> ${destination}`;
32
36
  }
33
37
 
34
38
  if (doesExist && mode === "skip-if-exists") {
35
- console.info(`[SKIPPED] ${dest} (exists)`);
39
+ console.info(`[SKIPPED] ${destination} (exists)`);
36
40
  return;
37
41
  }
38
42
 
39
- await writeFile(dest, new TextEncoder().encode(cntn));
40
- } satisfies Action<{ content?: string; destination?: string }>;
43
+ await writeFile(destination, new TextEncoder().encode(templateContent));
44
+ } satisfies Action<{ templateContent?: string; destination?: string }>;
41
45
  }
42
46
 
43
47
  function fileExists(destination: string) {
package/bun.lock ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "@codeandmoney/jargal",
6
+ "dependencies": {
7
+ "change-case": "^5.4.4",
8
+ "enquirer": "^2.4.1",
9
+ "es-toolkit": "^1.40.0",
10
+ "handlebars": "^4.7.8",
11
+ "title-case": "^4.3.2",
12
+ "valibot": "^1.1.0",
13
+ },
14
+ "devDependencies": {
15
+ "@types/bun": "^1.3.0",
16
+ "@types/node": "^24.7.2",
17
+ },
18
+ "peerDependencies": {
19
+ "typescript": "^5",
20
+ },
21
+ },
22
+ },
23
+ "packages": {
24
+ "@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
25
+
26
+ "@types/node": ["@types/node@24.7.2", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA=="],
27
+
28
+ "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
29
+
30
+ "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
31
+
32
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
33
+
34
+ "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
35
+
36
+ "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="],
37
+
38
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
39
+
40
+ "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
41
+
42
+ "es-toolkit": ["es-toolkit@1.40.0", "", {}, "sha512-8o6w0KFmU0CiIl0/Q/BCEOabF2IJaELM1T2PWj6e8KqzHv1gdx+7JtFnDwOx1kJH/isJ5NwlDG1nCr1HrRF94Q=="],
43
+
44
+ "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
45
+
46
+ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
47
+
48
+ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
49
+
50
+ "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
51
+
52
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
53
+
54
+ "title-case": ["title-case@4.3.2", "", {}, "sha512-I/nkcBo73mO42Idfv08jhInV61IMb61OdIFxk+B4Gu1oBjWBPOLmhZdsli+oJCVaD+86pYQA93cJfFt224ZFAA=="],
55
+
56
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
57
+
58
+ "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
59
+
60
+ "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
61
+
62
+ "valibot": ["valibot@1.1.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw=="],
63
+
64
+ "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
65
+
66
+ "bun-types/@types/node": ["@types/node@24.7.0", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw=="],
67
+ }
68
+ }
package/jargal.ts ADDED
@@ -0,0 +1,139 @@
1
+ import { merge, toMerged } from "es-toolkit";
2
+ import { Renderer } from "./renderer";
3
+ import { write } from "./actions/jargal-write";
4
+ import { resolve, dirname, join } from "path";
5
+ import { mkdir } from "fs/promises";
6
+ import { writeFile } from "fs/promises";
7
+
8
+ import { templates, type TemplatesMap } from "./actions/jargal-templates";
9
+ import { context } from "./actions/jargal-context";
10
+
11
+ export { templates, context };
12
+
13
+ export type RenderEntry = { control: "user"; data: any; pathTemplate: string; contentTemplate: string } | { control?: "auto"; data: any; destination: string };
14
+
15
+ export class Jargal<const in out Context> {
16
+ #context = {
17
+ templates: { default: {} },
18
+ renderData: [] as { baseSavePath: string; data: any }[],
19
+ renderedContent: [] as { savePath: string; content: string }[],
20
+ } as Context;
21
+
22
+ #renderer: Renderer;
23
+
24
+ get context() {
25
+ return this.#context;
26
+ }
27
+
28
+ constructor() {
29
+ this.#renderer = new Renderer();
30
+ }
31
+
32
+ setContext<Setter extends (...args: any) => any>(setter: Setter): Jargal<ReturnType<Setter> & Context>;
33
+ setContext<const Setter extends Record<string, any>>(setter: Setter): Jargal<Setter & Context>;
34
+ setContext(setter: any) {
35
+ if (typeof setter === "function") {
36
+ const context = setter(this.#context);
37
+ this.#context = toMerged(this.#context as any, context);
38
+ return this as any;
39
+ }
40
+
41
+ this.#context = toMerged(this.#context as any, setter);
42
+ return this as any;
43
+ }
44
+
45
+ render(params?: {
46
+ composeRenderData?: (ctx: Context) => RenderEntry[];
47
+ scope?: Context extends { templates: { [key: string]: any } } ? keyof Context["templates"] : "default";
48
+ }): Jargal<Context & { renderedContent: { savePath: string; content: string }[] }> {
49
+ const composeData: (ctx: Context) => RenderEntry[] =
50
+ params?.composeRenderData ??
51
+ // @ts-expect-error
52
+ ((ctx: Context) => ctx.renderData);
53
+
54
+ // @ts-expect-error
55
+ const templatesToRender = this.#context.templates[params?.scope ?? "default"] as TemplatesMap;
56
+
57
+ const composedData = composeData(this.#context);
58
+
59
+ for (const renderEntry of composedData) {
60
+ if (!renderEntry.control || renderEntry.control === "auto") {
61
+ for (const [filename, template] of Object.entries(templatesToRender)) {
62
+ // @ts-expect-error
63
+ this.#context.renderedContent.push({
64
+ savePath: this.#renderer.renderString({ template: join(renderEntry.destination, template.savePath), data: renderEntry.data }),
65
+ content: this.#renderer.renderString({ template: template.templateContent, data: renderEntry.data }),
66
+ });
67
+ }
68
+
69
+ continue;
70
+ }
71
+
72
+ if (renderEntry.control === "user") {
73
+ // @ts-expect-error
74
+ this.#context.renderedContent.push({
75
+ savePath: this.#renderer.renderString({ template: renderEntry.pathTemplate, data: renderEntry.data }),
76
+ content: this.#renderer.renderString({ template: renderEntry.contentTemplate, data: renderEntry.data }),
77
+ });
78
+
79
+ continue;
80
+ }
81
+ }
82
+
83
+ // const entryContentTemplate = renderEntry.contentTemplate;
84
+
85
+ // let renderedContent: string | undefined = undefined;
86
+ // let renderedPath: string | undefined = undefined;
87
+
88
+ // if (renderEntry.contentTemplate) {
89
+ // const templateToRender =
90
+ // // @ts-expect-error
91
+ // (this.#context.templates[params?.scope ?? "default"] as TemplatesMap)[renderEntry.contentTemplate]?.templateContent || renderEntry.contentTemplate;
92
+
93
+ // renderedContent = this.#renderer.renderString({ template: templateToRender, data: renderEntry.data });
94
+ // continue;
95
+ // }
96
+
97
+ // if (renderEntry.pathTemplate) {
98
+ // renderedPath = this.#renderer.renderString({ template: renderEntry.pathTemplate, data: renderEntry.data });
99
+ // continue;
100
+ // }
101
+
102
+ // for (const [filename, template] of Object.entries(templatesToRender)) {
103
+ // // console.log({ renderEntry, template });
104
+
105
+ // // @ts-expect-error
106
+ // this.#context.renderedContent.push({
107
+ // savePath:
108
+ // renderedPath ||
109
+ // this.#renderer.renderString({
110
+ // template: renderEntry.baseSavePath ?? (renderEntry?.fullSavePath ? renderEntry?.fullSavePath : join(renderEntry.baseSavePath, template.savePath)),
111
+ // data: renderEntry.data,
112
+ // }),
113
+ // content:
114
+ // renderedContent || this.#renderer.renderString({ template: renderEntry?.contentTemplate ?? template.templateContent, data: renderEntry.data }),
115
+ // });
116
+ // }
117
+
118
+ return this as any;
119
+ }
120
+
121
+ async write(write?: (params: { savePath: string; content: string }) => void | Promise<void>): Promise<void> {
122
+ if (typeof write === "function") {
123
+ // @ts-expect-error
124
+ for (const renderConfig of this.#context.renderedContent as { savePath: string; content: string }[]) {
125
+ // console.log({ renderConfig })
126
+ await write(renderConfig);
127
+ }
128
+
129
+ return;
130
+ }
131
+
132
+ // console.log({ renderedContent: this.#context.renderedContent})
133
+ // @ts-expect-error
134
+ for (const renderConfig of this.#context.renderedContent as { savePath: string; content: string }[]) {
135
+ await mkdir(dirname(renderConfig.savePath), { recursive: true });
136
+ await writeFile(renderConfig.savePath, new TextEncoder().encode(renderConfig.content), {});
137
+ }
138
+ }
139
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeandmoney/jargal",
3
- "version": "0.0.0-rc.1",
3
+ "version": "0.0.0-rc.4",
4
4
  "description": "Renderer",
5
5
  "license": "MIT",
6
6
  "author": "Code & Money Team",
@@ -12,7 +12,8 @@
12
12
  }
13
13
  ],
14
14
  "exports": {
15
- ".": "./exports.ts",
15
+ "./old": "./exports.ts",
16
+ ".": "./jargal.ts",
16
17
  "./types": "./types.ts",
17
18
  "./actions": "./actions/exports.ts"
18
19
  },
package/renderer.ts CHANGED
@@ -31,10 +31,10 @@ export class Renderer {
31
31
  return this;
32
32
  }
33
33
 
34
- public readonly partials: Partials = readonly(this.#partials);
35
- public readonly helpers: Helpers = readonly(this.#helpers);
34
+ private readonly partials: Partials = readonly(this.#partials);
35
+ private readonly helpers: Helpers = readonly(this.#helpers);
36
36
 
37
- public get<T extends SetterScope>(scope: T, name: string): GetReturnType<T> {
37
+ private get<T extends SetterScope>(scope: T, name: string): GetReturnType<T> {
38
38
  const target = get(this.#mapping, scope);
39
39
 
40
40
  if (!target) {
@@ -44,7 +44,7 @@ export class Renderer {
44
44
  return get(target, name);
45
45
  }
46
46
 
47
- public list<T extends `${SetterScope}s`, O extends boolean = false>(
47
+ private list<T extends `${SetterScope}s`, O extends boolean = false>(
48
48
  scope: T,
49
49
  options?: { full?: O },
50
50
  ): O extends true ? MappingScope[T extends `${infer S}s` ? S : T][] : string[] {
package/runner.ts CHANGED
@@ -2,7 +2,7 @@ import { Renderer } from "./renderer.ts";
2
2
  import * as v from "valibot";
3
3
  import type { Action, Config, ExecuteActionParams, GeneratorParams } from "./types.ts";
4
4
  import { selectGenerator } from "./actions/select-generator.ts";
5
- import assert from "node:assert";
5
+ import assert from "node:assert/strict";
6
6
 
7
7
  export async function runGenerator({ context, generator, renderer }: GeneratorParams): Promise<void> {
8
8
  assert(generator);
@@ -60,8 +60,6 @@ async function execRecursive(executed: Action | Action[], { context, renderer }:
60
60
  return await executeAction({ action: executed, context, renderer });
61
61
  }
62
62
 
63
- // assert(!executed);
64
-
65
63
  return undefined;
66
64
  }
67
65
 
@@ -78,8 +76,8 @@ const ConfigSchema = v.object({
78
76
  ),
79
77
  });
80
78
 
81
- export async function run(config_: Config): Promise<void> {
82
- const config = v.parse(ConfigSchema, config_);
79
+ export async function run(runConfig: Config): Promise<void> {
80
+ const config = v.run(ConfigSchema, runConfig);
83
81
 
84
82
  if (config.generators.length === 1) {
85
83
  const generator = config.generators[0];
@@ -96,9 +94,6 @@ export async function run(config_: Config): Promise<void> {
96
94
  return await runGenerator({
97
95
  context: { errors: [], answers: {} },
98
96
  renderer: new Renderer(),
99
- generator: {
100
- name: "select",
101
- actions: [selectGenerator(config)],
102
- },
97
+ generator: { name: "select", actions: [selectGenerator(config)] },
103
98
  });
104
99
  }
package/types.ts CHANGED
@@ -4,11 +4,10 @@ export interface Config {
4
4
  generators: GeneratorConfig[];
5
5
  }
6
6
 
7
- export interface Context extends Record<string, unknown> {
7
+ export type Context<Ctx extends object = {}> = Ctx & {
8
8
  answers: Record<string, string | boolean | (string | boolean)[]>;
9
9
  errors: Error[];
10
- context: Record<string, any>;
11
- }
10
+ };
12
11
 
13
12
  export type DeepReadonly<T> = {
14
13
  readonly [Key in keyof T]: T[Key] extends any[] ? T[Key] : T[Key] extends object ? DeepReadonly<T[Key]> : T[Key];
@@ -21,31 +20,31 @@ export interface GeneratorParams {
21
20
  hooks?: ActionHooks;
22
21
  }
23
22
 
24
- export interface ActionParams {
25
- context: Context;
23
+ export interface ActionParams<Ctx extends object = {}> {
24
+ context: Context<Ctx>;
26
25
  renderer: Renderer;
27
26
  hooks?: ActionHooks;
28
27
  }
29
28
 
30
- export interface ExecuteActionParams {
31
- context: Context;
29
+ export interface ExecuteActionParams<Ctx extends object = {}> {
30
+ context: Context<Ctx>;
32
31
  renderer: Renderer;
33
32
  action: Action;
34
33
  }
35
34
 
35
+ export type ContextAction<Ctx extends object = {}> = (params: DeepReadonly<ExecuteActionParams<Ctx>>) => Partial<Context<Ctx>> | Promise<Partial<Context<Ctx>>>;
36
+
36
37
  export interface GeneratorConfig {
37
38
  name: string;
38
- // TODO: implement commented out interface
39
- description?: string; // | (( params: unknown ) => string)
40
- // TODO: implement commented out interface
41
- actions: Action[]; // | Promise<Action[]> | (( params: unknown ) => Action[] | Promise<Action[]>)
39
+ description?: string;
40
+ actions: Action[];
42
41
  }
43
42
 
44
43
  export type HelperFn = (str: string) => string;
45
44
 
46
45
  export interface TextHelpers extends Record<string, HelperFn> {}
47
46
 
48
- export type Action<T extends object = {}> = (params: ActionParams & T) => void | Action | Action[] | Promise<void | Action | Action[]>;
47
+ export type Action<Ctx extends object = {}> = (params: ActionParams<Ctx>) => void | Action | Action[] | Promise<void | Action | Action[]>;
49
48
 
50
49
  export interface ActionHooksFailures {
51
50
  path: string;
@@ -90,5 +89,3 @@ export type GetReturnType<T extends SetterScope> = Mapping[T];
90
89
 
91
90
  export interface Partials extends Record<string, string> {}
92
91
  export interface Helpers extends Record<string, any> {}
93
-
94
- export type ContextAction = (context: DeepReadonly<Context>) => Partial<Context> | Promise<Partial<Context>>;