wp-typia 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ import { z } from "zod";
2
+
3
+ import {
4
+ FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
5
+ FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT,
6
+ FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT,
7
+ getFirstPartyScrollTop,
8
+ getFirstPartyViewportHeight,
9
+ } from "./first-party-form-model";
10
+
11
+ export const createFlowSchema = z.object({
12
+ "data-storage": z.string().optional(),
13
+ namespace: z.string().optional(),
14
+ "no-install": z.boolean().default(false),
15
+ "package-manager": z.string().optional(),
16
+ "persistence-policy": z.string().optional(),
17
+ "php-prefix": z.string().optional(),
18
+ "project-dir": z.string().min(1),
19
+ template: z.string().optional(),
20
+ "text-domain": z.string().optional(),
21
+ variant: z.string().optional(),
22
+ "with-migration-ui": z.boolean().default(false),
23
+ "with-test-preset": z.boolean().default(false),
24
+ "with-wp-env": z.boolean().default(false),
25
+ yes: z.boolean().default(false),
26
+ });
27
+
28
+ export type CreateFlowValues = z.infer<typeof createFlowSchema>;
29
+
30
+ export type CreateFieldName =
31
+ | "project-dir"
32
+ | "template"
33
+ | "package-manager"
34
+ | "namespace"
35
+ | "text-domain"
36
+ | "php-prefix"
37
+ | "data-storage"
38
+ | "persistence-policy"
39
+ | "no-install"
40
+ | "yes"
41
+ | "with-wp-env"
42
+ | "with-test-preset"
43
+ | "with-migration-ui";
44
+
45
+ export const CREATE_CHECKBOX_FIELD_NAMES = [
46
+ "no-install",
47
+ "yes",
48
+ "with-wp-env",
49
+ "with-test-preset",
50
+ "with-migration-ui",
51
+ ] as const satisfies ReadonlyArray<CreateFieldName>;
52
+
53
+ export const CREATE_FIELD_ORDER = [
54
+ "project-dir",
55
+ "template",
56
+ "package-manager",
57
+ "namespace",
58
+ "text-domain",
59
+ "php-prefix",
60
+ "data-storage",
61
+ "persistence-policy",
62
+ ...CREATE_CHECKBOX_FIELD_NAMES,
63
+ ] as const satisfies ReadonlyArray<CreateFieldName>;
64
+
65
+ const CREATE_FIELD_HEIGHTS: Record<CreateFieldName, number> = {
66
+ "data-storage": FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT,
67
+ namespace: FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT,
68
+ "no-install": FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
69
+ "package-manager": FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT,
70
+ "persistence-policy": FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT,
71
+ "php-prefix": FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT,
72
+ "project-dir": FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT,
73
+ template: FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT,
74
+ "text-domain": FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT,
75
+ yes: FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
76
+ "with-migration-ui": FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
77
+ "with-test-preset": FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
78
+ "with-wp-env": FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT,
79
+ };
80
+
81
+ export function isCreatePersistenceTemplate(template?: string): boolean {
82
+ return template === "persistence" || template === "compound";
83
+ }
84
+
85
+ export function getVisibleCreateFieldNames(
86
+ values: Partial<CreateFlowValues>,
87
+ ): Array<CreateFieldName> {
88
+ return CREATE_FIELD_ORDER.filter((name) => {
89
+ if (name === "data-storage" || name === "persistence-policy") {
90
+ return isCreatePersistenceTemplate(values.template);
91
+ }
92
+
93
+ return true;
94
+ });
95
+ }
96
+
97
+ export function getCreateViewportHeight(terminalHeight = 24): number {
98
+ return getFirstPartyViewportHeight(terminalHeight);
99
+ }
100
+
101
+ export function getCreateScrollTop(options: {
102
+ activeFieldName: string | null;
103
+ values: Partial<CreateFlowValues>;
104
+ viewportHeight: number;
105
+ }): number {
106
+ const { activeFieldName, values, viewportHeight } = options;
107
+ return getFirstPartyScrollTop({
108
+ activeFieldName,
109
+ fieldHeights: CREATE_FIELD_HEIGHTS,
110
+ visibleFieldNames: getVisibleCreateFieldNames(values),
111
+ viewportHeight,
112
+ });
113
+ }
114
+
115
+ export function sanitizeCreateSubmitValues(values: CreateFlowValues): CreateFlowValues {
116
+ if (isCreatePersistenceTemplate(values.template)) {
117
+ return values;
118
+ }
119
+
120
+ return {
121
+ ...values,
122
+ "data-storage": undefined,
123
+ "persistence-policy": undefined,
124
+ };
125
+ }
@@ -1,33 +1,165 @@
1
- import { SchemaForm } from "@bunli/tui";
2
- import { z } from "zod";
1
+ import { createElement, useMemo } from "react";
2
+
3
+ import {
4
+ Form,
5
+ type SelectOption,
6
+ useFormContext,
7
+ useTerminalDimensions,
8
+ } from "@bunli/tui";
3
9
 
4
10
  import { executeCreateCommand } from "../runtime-bridge";
5
11
  import { useAlternateBufferLifecycle } from "./alternate-buffer-lifecycle";
12
+ import {
13
+ type CreateFlowValues,
14
+ CREATE_CHECKBOX_FIELD_NAMES,
15
+ createFlowSchema,
16
+ getCreateScrollTop,
17
+ getCreateViewportHeight,
18
+ getVisibleCreateFieldNames,
19
+ isCreatePersistenceTemplate,
20
+ sanitizeCreateSubmitValues,
21
+ } from "./create-flow-model";
22
+ import {
23
+ FirstPartyCheckboxField,
24
+ FirstPartyScrollBox,
25
+ FirstPartySelectField,
26
+ FirstPartyTextField,
27
+ } from "./first-party-form";
28
+ import { getWrappedFieldNeighbors } from "./first-party-form-model";
29
+
30
+ const templateOptions: SelectOption[] = [
31
+ { description: "Basic block scaffold", name: "basic", value: "basic" },
32
+ { description: "Interactivity API block scaffold", name: "interactivity", value: "interactivity" },
33
+ { description: "Persistence-enabled block scaffold", name: "persistence", value: "persistence" },
34
+ { description: "Compound parent + child scaffold", name: "compound", value: "compound" },
35
+ { description: "Official empty workspace template", name: "workspace", value: "workspace" },
36
+ ];
37
+
38
+ const packageManagerOptions: SelectOption[] = [
39
+ { description: "Use npm", name: "npm", value: "npm" },
40
+ { description: "Use pnpm", name: "pnpm", value: "pnpm" },
41
+ { description: "Use yarn", name: "yarn", value: "yarn" },
42
+ { description: "Use bun", name: "bun", value: "bun" },
43
+ ];
44
+
45
+ const dataStorageOptions: SelectOption[] = [
46
+ { description: "Dedicated custom table storage", name: "custom-table", value: "custom-table" },
47
+ { description: "Persist through post meta", name: "post-meta", value: "post-meta" },
48
+ ];
6
49
 
7
- const createFlowSchema = z.object({
8
- "data-storage": z.string().optional(),
9
- namespace: z.string().optional(),
10
- "no-install": z.boolean().default(false),
11
- "package-manager": z.string().optional(),
12
- "persistence-policy": z.string().optional(),
13
- "php-prefix": z.string().optional(),
14
- "project-dir": z.string().min(1),
15
- template: z.string().optional(),
16
- "text-domain": z.string().optional(),
17
- variant: z.string().optional(),
18
- "with-migration-ui": z.boolean().default(false),
19
- "with-test-preset": z.boolean().default(false),
20
- "with-wp-env": z.boolean().default(false),
21
- yes: z.boolean().default(false),
22
- });
50
+ const persistencePolicyOptions: SelectOption[] = [
51
+ { description: "Authenticated write policy", name: "authenticated", value: "authenticated" },
52
+ { description: "Public token policy", name: "public", value: "public" },
53
+ ];
23
54
 
24
- type CreateFlowValues = z.infer<typeof createFlowSchema>;
55
+ const checkboxLabels: Record<(typeof CREATE_CHECKBOX_FIELD_NAMES)[number], string> = {
56
+ "no-install": "Skip dependency install",
57
+ yes: "Use defaults without prompts",
58
+ "with-wp-env": "Add wp-env preset",
59
+ "with-test-preset": "Add test preset",
60
+ "with-migration-ui": "Add migration UI",
61
+ };
25
62
 
26
63
  type CreateFlowProps = {
27
64
  cwd: string;
28
65
  initialValues: Partial<CreateFlowValues>;
29
66
  };
30
67
 
68
+ type CreateSelectFieldName = {
69
+ [K in keyof CreateFlowValues]-?: CreateFlowValues[K] extends string | undefined ? K : never;
70
+ }[keyof CreateFlowValues];
71
+
72
+ function CreateFlowFields() {
73
+ const { activeFieldName, values } = useFormContext();
74
+ const { height: terminalHeight = 24 } = useTerminalDimensions();
75
+ const createValues = values as Partial<CreateFlowValues>;
76
+ const template = createValues.template;
77
+ const viewportHeight = getCreateViewportHeight(terminalHeight);
78
+ const visibleFields = useMemo(() => getVisibleCreateFieldNames(createValues), [createValues]);
79
+ const scrollValues = useMemo(() => ({ template }), [template]);
80
+ const scrollTop = useMemo(
81
+ () =>
82
+ getCreateScrollTop({
83
+ activeFieldName,
84
+ values: scrollValues,
85
+ viewportHeight,
86
+ }),
87
+ [activeFieldName, scrollValues, viewportHeight],
88
+ );
89
+
90
+ return createElement(
91
+ FirstPartyScrollBox,
92
+ { scrollTop, viewportHeight },
93
+ [
94
+ createElement(FirstPartyTextField, {
95
+ ...getWrappedFieldNeighbors(visibleFields, "project-dir"),
96
+ key: "project-dir",
97
+ label: "Project directory",
98
+ name: "project-dir",
99
+ required: true,
100
+ }),
101
+ createElement(FirstPartySelectField, {
102
+ ...getWrappedFieldNeighbors(visibleFields, "template"),
103
+ key: "template",
104
+ label: "Template",
105
+ name: "template" satisfies CreateSelectFieldName,
106
+ options: templateOptions,
107
+ }),
108
+ createElement(FirstPartySelectField, {
109
+ ...getWrappedFieldNeighbors(visibleFields, "package-manager"),
110
+ key: "package-manager",
111
+ label: "Package manager",
112
+ name: "package-manager" satisfies CreateSelectFieldName,
113
+ options: packageManagerOptions,
114
+ }),
115
+ createElement(FirstPartyTextField, {
116
+ ...getWrappedFieldNeighbors(visibleFields, "namespace"),
117
+ key: "namespace",
118
+ label: "Namespace",
119
+ name: "namespace",
120
+ }),
121
+ createElement(FirstPartyTextField, {
122
+ ...getWrappedFieldNeighbors(visibleFields, "text-domain"),
123
+ key: "text-domain",
124
+ label: "Text domain",
125
+ name: "text-domain",
126
+ }),
127
+ createElement(FirstPartyTextField, {
128
+ ...getWrappedFieldNeighbors(visibleFields, "php-prefix"),
129
+ key: "php-prefix",
130
+ label: "PHP prefix",
131
+ name: "php-prefix",
132
+ }),
133
+ isCreatePersistenceTemplate(template)
134
+ ? createElement(FirstPartySelectField, {
135
+ ...getWrappedFieldNeighbors(visibleFields, "data-storage"),
136
+ key: "data-storage",
137
+ label: "Data storage",
138
+ name: "data-storage" satisfies CreateSelectFieldName,
139
+ options: dataStorageOptions,
140
+ })
141
+ : null,
142
+ isCreatePersistenceTemplate(template)
143
+ ? createElement(FirstPartySelectField, {
144
+ ...getWrappedFieldNeighbors(visibleFields, "persistence-policy"),
145
+ key: "persistence-policy",
146
+ label: "Persistence policy",
147
+ name: "persistence-policy" satisfies CreateSelectFieldName,
148
+ options: persistencePolicyOptions,
149
+ })
150
+ : null,
151
+ ...CREATE_CHECKBOX_FIELD_NAMES.map((name) =>
152
+ createElement(FirstPartyCheckboxField, {
153
+ ...getWrappedFieldNeighbors(visibleFields, name),
154
+ key: name,
155
+ label: checkboxLabels[name],
156
+ name,
157
+ }),
158
+ ),
159
+ ],
160
+ );
161
+ }
162
+
31
163
  export function CreateFlow({ cwd, initialValues }: CreateFlowProps) {
32
164
  const { handleCancel, handleSubmit } = useAlternateBufferLifecycle("wp-typia create failed");
33
165
  const defaultPrompt = {
@@ -42,114 +174,25 @@ export function CreateFlow({ cwd, initialValues }: CreateFlowProps) {
42
174
  };
43
175
 
44
176
  return (
45
- <SchemaForm
46
- fields={[
47
- { kind: "text", label: "Project directory", name: "project-dir", required: true },
48
- {
49
- kind: "select",
50
- label: "Template",
51
- name: "template",
52
- options: [
53
- { name: "basic", description: "Basic block scaffold", value: "basic" },
54
- {
55
- name: "interactivity",
56
- description: "Interactivity API block scaffold",
57
- value: "interactivity",
58
- },
59
- {
60
- name: "persistence",
61
- description: "Persistence-enabled block scaffold",
62
- value: "persistence",
63
- },
64
- {
65
- name: "compound",
66
- description: "Compound parent + child scaffold",
67
- value: "compound",
68
- },
69
- {
70
- name: "@wp-typia/create-workspace-template",
71
- description: "Official empty workspace template",
72
- value: "@wp-typia/create-workspace-template",
73
- },
74
- ],
75
- },
76
- {
77
- kind: "select",
78
- label: "Package manager",
79
- name: "package-manager",
80
- options: [
81
- { name: "npm", description: "Use npm", value: "npm" },
82
- { name: "pnpm", description: "Use pnpm", value: "pnpm" },
83
- { name: "yarn", description: "Use yarn", value: "yarn" },
84
- { name: "bun", description: "Use bun", value: "bun" },
85
- ],
86
- },
87
- { kind: "text", label: "Namespace", name: "namespace" },
88
- { kind: "text", label: "Text domain", name: "text-domain" },
89
- { kind: "text", label: "PHP prefix", name: "php-prefix" },
90
- {
91
- kind: "select",
92
- label: "Data storage",
93
- name: "data-storage",
94
- options: [
95
- {
96
- name: "custom-table",
97
- description: "Dedicated custom table storage",
98
- value: "custom-table",
99
- },
100
- {
101
- name: "post-meta",
102
- description: "Persist through post meta",
103
- value: "post-meta",
104
- },
105
- ],
106
- visibleWhen: (values) =>
107
- values.template === "persistence" || values.template === "compound",
108
- },
109
- {
110
- kind: "select",
111
- label: "Persistence policy",
112
- name: "persistence-policy",
113
- options: [
114
- {
115
- name: "authenticated",
116
- description: "Authenticated write policy",
117
- value: "authenticated",
118
- },
119
- { name: "public", description: "Public token policy", value: "public" },
120
- ],
121
- visibleWhen: (values) =>
122
- values.template === "persistence" || values.template === "compound",
123
- },
124
- { kind: "checkbox", label: "Skip dependency install", name: "no-install" },
125
- { kind: "checkbox", label: "Use defaults without prompts", name: "yes" },
126
- { kind: "checkbox", label: "Add wp-env preset", name: "with-wp-env" },
127
- {
128
- kind: "checkbox",
129
- label: "Add test preset",
130
- name: "with-test-preset",
131
- },
132
- {
133
- kind: "checkbox",
134
- label: "Add migration UI",
135
- name: "with-migration-ui",
136
- },
137
- ]}
177
+ <Form
138
178
  initialValues={initialValues}
139
179
  onCancel={handleCancel}
140
180
  onSubmit={async (values) =>
141
181
  handleSubmit(async () => {
142
- await executeCreateCommand({
143
- cwd,
144
- flags: values,
145
- interactive: true,
146
- projectDir: values["project-dir"],
147
- prompt: defaultPrompt,
148
- });
182
+ const flags = sanitizeCreateSubmitValues(values);
183
+ await executeCreateCommand({
184
+ cwd,
185
+ flags,
186
+ interactive: true,
187
+ projectDir: values["project-dir"],
188
+ prompt: defaultPrompt,
189
+ });
149
190
  })
150
191
  }
151
192
  schema={createFlowSchema}
152
193
  title="Create a wp-typia project"
153
- />
194
+ >
195
+ <CreateFlowFields />
196
+ </Form>
154
197
  );
155
198
  }
@@ -0,0 +1,62 @@
1
+ export const FIRST_PARTY_FIELD_GAP = 1;
2
+ export const FIRST_PARTY_TEXT_FIELD_BODY_HEIGHT = 6;
3
+ export const FIRST_PARTY_CHECKBOX_FIELD_BODY_HEIGHT = 2;
4
+ export const FIRST_PARTY_SELECT_FIELD_LABEL_GAP = 1;
5
+ export const FIRST_PARTY_SELECT_FIELD_CONTROL_HEIGHT = 3;
6
+ export const FIRST_PARTY_SELECT_FIELD_BODY_HEIGHT =
7
+ 1 + FIRST_PARTY_SELECT_FIELD_LABEL_GAP + FIRST_PARTY_SELECT_FIELD_CONTROL_HEIGHT + 1;
8
+
9
+ export type FirstPartyFieldHeights<TName extends string> = Record<TName, number>;
10
+
11
+ export function getWrappedFieldNeighbors<TName extends string>(
12
+ visibleFieldNames: ReadonlyArray<TName>,
13
+ fieldName: TName,
14
+ ): {
15
+ nextFieldName?: TName;
16
+ previousFieldName?: TName;
17
+ } {
18
+ const index = visibleFieldNames.indexOf(fieldName);
19
+ if (index === -1 || visibleFieldNames.length < 2) {
20
+ return {};
21
+ }
22
+
23
+ return {
24
+ nextFieldName: visibleFieldNames[(index + 1) % visibleFieldNames.length],
25
+ previousFieldName:
26
+ visibleFieldNames[(index - 1 + visibleFieldNames.length) % visibleFieldNames.length],
27
+ };
28
+ }
29
+
30
+ export function getFirstPartyViewportHeight(terminalHeight = 24): number {
31
+ return Math.max(8, Math.min(28, terminalHeight - 12));
32
+ }
33
+
34
+ export function getFirstPartyScrollTop<TName extends string>(options: {
35
+ activeFieldName: string | null;
36
+ fieldHeights: FirstPartyFieldHeights<TName>;
37
+ visibleFieldNames: ReadonlyArray<TName>;
38
+ viewportHeight: number;
39
+ }): number {
40
+ const { activeFieldName, fieldHeights, visibleFieldNames, viewportHeight } = options;
41
+ if (!activeFieldName) {
42
+ return 0;
43
+ }
44
+
45
+ let offset = 0;
46
+ for (const fieldName of visibleFieldNames) {
47
+ const fieldHeight = fieldHeights[fieldName];
48
+ if (fieldName === activeFieldName) {
49
+ const safeViewportHeight = Math.max(4, viewportHeight - 2);
50
+ const fieldBottom = offset + fieldHeight;
51
+ if (fieldBottom <= safeViewportHeight) {
52
+ return 0;
53
+ }
54
+
55
+ return Math.max(0, fieldBottom - safeViewportHeight + 1);
56
+ }
57
+
58
+ offset += fieldHeight + FIRST_PARTY_FIELD_GAP;
59
+ }
60
+
61
+ return 0;
62
+ }