wp-typia 0.16.0 → 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.
- package/package.json +2 -2
- package/src/ui/README.md +26 -0
- package/src/ui/add-flow-model.ts +120 -0
- package/src/ui/add-flow.tsx +246 -183
- package/src/ui/alternate-buffer-lifecycle.ts +152 -0
- package/src/ui/create-flow-model.ts +125 -0
- package/src/ui/create-flow.tsx +172 -145
- package/src/ui/first-party-form-model.ts +62 -0
- package/src/ui/first-party-form.tsx +460 -0
- package/src/ui/lazy-flow.tsx +22 -17
- package/src/ui/migrate-flow-model.ts +126 -0
- package/src/ui/migrate-flow.tsx +177 -159
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
import { useRuntime } from "@bunli/runtime/app";
|
|
4
|
+
import { useKeyboard } from "@bunli/tui";
|
|
5
|
+
|
|
6
|
+
type AlternateBufferKeyEvent = {
|
|
7
|
+
ctrl?: boolean;
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type AlternateBufferFailureOptions = {
|
|
12
|
+
context: string;
|
|
13
|
+
error: unknown;
|
|
14
|
+
exit: () => void;
|
|
15
|
+
log?: (message: string) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type RunAlternateBufferActionOptions = {
|
|
19
|
+
action: () => Promise<void>;
|
|
20
|
+
context: string;
|
|
21
|
+
exit: () => void;
|
|
22
|
+
log?: (message: string) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function describeAlternateBufferFailure(context: string, error: unknown): string {
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
return `${context}: ${message}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isAlternateBufferExitKey(key: AlternateBufferKeyEvent): boolean {
|
|
31
|
+
return key.name === "q" || (key.ctrl === true && key.name === "c");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function reportAlternateBufferFailure({
|
|
35
|
+
context,
|
|
36
|
+
error,
|
|
37
|
+
exit,
|
|
38
|
+
log = console.error,
|
|
39
|
+
}: AlternateBufferFailureOptions): void {
|
|
40
|
+
const message = describeAlternateBufferFailure(context, error);
|
|
41
|
+
exit();
|
|
42
|
+
log(message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runAlternateBufferAction({
|
|
46
|
+
action,
|
|
47
|
+
context,
|
|
48
|
+
exit,
|
|
49
|
+
log = console.error,
|
|
50
|
+
}: RunAlternateBufferActionOptions): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
await action();
|
|
53
|
+
exit();
|
|
54
|
+
} catch (error) {
|
|
55
|
+
reportAlternateBufferFailure({ context, error, exit, log });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function resolveLazyFlowComponent<TProps>({
|
|
60
|
+
loader,
|
|
61
|
+
onLoaded,
|
|
62
|
+
onFailure,
|
|
63
|
+
isDisposed,
|
|
64
|
+
}: {
|
|
65
|
+
loader: () => Promise<{ default: React.ComponentType<TProps> }>;
|
|
66
|
+
onLoaded: (component: React.ComponentType<TProps>) => void;
|
|
67
|
+
onFailure: (error: unknown) => void;
|
|
68
|
+
isDisposed: () => boolean;
|
|
69
|
+
}): Promise<void> {
|
|
70
|
+
try {
|
|
71
|
+
const module = await loader();
|
|
72
|
+
if (!isDisposed()) {
|
|
73
|
+
onLoaded(module.default);
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (!isDisposed()) {
|
|
77
|
+
onFailure(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useAlternateBufferExitKeys(options: {
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
exit?: () => void;
|
|
85
|
+
} = {}): void {
|
|
86
|
+
const runtime = useRuntime();
|
|
87
|
+
const exit = options.exit ?? (() => runtime.exit());
|
|
88
|
+
const enabled = options.enabled ?? true;
|
|
89
|
+
|
|
90
|
+
useKeyboard((key: AlternateBufferKeyEvent) => {
|
|
91
|
+
if (!enabled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isAlternateBufferExitKey(key)) {
|
|
96
|
+
exit();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function useAlternateBufferLifecycle(
|
|
102
|
+
context: string,
|
|
103
|
+
options: {
|
|
104
|
+
enableExitKeys?: boolean;
|
|
105
|
+
} = {},
|
|
106
|
+
): {
|
|
107
|
+
handleCancel: () => void;
|
|
108
|
+
handleFailure: (error: unknown) => void;
|
|
109
|
+
handleSubmit: (action: () => Promise<void>) => Promise<void>;
|
|
110
|
+
} {
|
|
111
|
+
const runtime = useRuntime();
|
|
112
|
+
const exit = useCallback(() => {
|
|
113
|
+
runtime.exit();
|
|
114
|
+
}, [runtime]);
|
|
115
|
+
|
|
116
|
+
useAlternateBufferExitKeys({
|
|
117
|
+
enabled: options.enableExitKeys ?? true,
|
|
118
|
+
exit,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const handleCancel = useCallback(() => {
|
|
122
|
+
exit();
|
|
123
|
+
}, [exit]);
|
|
124
|
+
|
|
125
|
+
const handleFailure = useCallback(
|
|
126
|
+
(error: unknown) => {
|
|
127
|
+
reportAlternateBufferFailure({
|
|
128
|
+
context,
|
|
129
|
+
error,
|
|
130
|
+
exit,
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
[context, exit],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const handleSubmit = useCallback(
|
|
137
|
+
async (action: () => Promise<void>) => {
|
|
138
|
+
await runAlternateBufferAction({
|
|
139
|
+
action,
|
|
140
|
+
context,
|
|
141
|
+
exit,
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
[context, exit],
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
handleCancel,
|
|
149
|
+
handleFailure,
|
|
150
|
+
handleSubmit,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -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
|
+
}
|
package/src/ui/create-flow.tsx
CHANGED
|
@@ -1,38 +1,167 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createElement, useMemo } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
Form,
|
|
5
|
+
type SelectOption,
|
|
6
|
+
useFormContext,
|
|
7
|
+
useTerminalDimensions,
|
|
8
|
+
} from "@bunli/tui";
|
|
6
9
|
|
|
7
10
|
import { executeCreateCommand } from "../runtime-bridge";
|
|
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";
|
|
8
29
|
|
|
9
|
-
const
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"project-dir": z.string().min(1),
|
|
17
|
-
template: z.string().optional(),
|
|
18
|
-
"text-domain": z.string().optional(),
|
|
19
|
-
variant: z.string().optional(),
|
|
20
|
-
"with-migration-ui": z.boolean().default(false),
|
|
21
|
-
"with-test-preset": z.boolean().default(false),
|
|
22
|
-
"with-wp-env": z.boolean().default(false),
|
|
23
|
-
yes: z.boolean().default(false),
|
|
24
|
-
});
|
|
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
|
+
];
|
|
25
37
|
|
|
26
|
-
|
|
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
|
+
];
|
|
49
|
+
|
|
50
|
+
const persistencePolicyOptions: SelectOption[] = [
|
|
51
|
+
{ description: "Authenticated write policy", name: "authenticated", value: "authenticated" },
|
|
52
|
+
{ description: "Public token policy", name: "public", value: "public" },
|
|
53
|
+
];
|
|
54
|
+
|
|
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
|
+
};
|
|
27
62
|
|
|
28
63
|
type CreateFlowProps = {
|
|
29
64
|
cwd: string;
|
|
30
65
|
initialValues: Partial<CreateFlowValues>;
|
|
31
66
|
};
|
|
32
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
|
+
|
|
33
163
|
export function CreateFlow({ cwd, initialValues }: CreateFlowProps) {
|
|
34
|
-
const
|
|
35
|
-
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
164
|
+
const { handleCancel, handleSubmit } = useAlternateBufferLifecycle("wp-typia create failed");
|
|
36
165
|
const defaultPrompt = {
|
|
37
166
|
close() {},
|
|
38
167
|
select<T extends string>(_message: string, options: Array<{ value: T }>, defaultValue = 1) {
|
|
@@ -45,127 +174,25 @@ export function CreateFlow({ cwd, initialValues }: CreateFlowProps) {
|
|
|
45
174
|
};
|
|
46
175
|
|
|
47
176
|
return (
|
|
48
|
-
|
|
49
|
-
{
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
value: "interactivity",
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: "persistence",
|
|
72
|
-
description: "Persistence-enabled block scaffold",
|
|
73
|
-
value: "persistence",
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: "compound",
|
|
77
|
-
description: "Compound parent + child scaffold",
|
|
78
|
-
value: "compound",
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: "@wp-typia/create-workspace-template",
|
|
82
|
-
description: "Official empty workspace template",
|
|
83
|
-
value: "@wp-typia/create-workspace-template",
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
kind: "select",
|
|
89
|
-
label: "Package manager",
|
|
90
|
-
name: "package-manager",
|
|
91
|
-
options: [
|
|
92
|
-
{ name: "npm", description: "Use npm", value: "npm" },
|
|
93
|
-
{ name: "pnpm", description: "Use pnpm", value: "pnpm" },
|
|
94
|
-
{ name: "yarn", description: "Use yarn", value: "yarn" },
|
|
95
|
-
{ name: "bun", description: "Use bun", value: "bun" },
|
|
96
|
-
],
|
|
97
|
-
},
|
|
98
|
-
{ kind: "text", label: "Namespace", name: "namespace" },
|
|
99
|
-
{ kind: "text", label: "Text domain", name: "text-domain" },
|
|
100
|
-
{ kind: "text", label: "PHP prefix", name: "php-prefix" },
|
|
101
|
-
{
|
|
102
|
-
kind: "select",
|
|
103
|
-
label: "Data storage",
|
|
104
|
-
name: "data-storage",
|
|
105
|
-
options: [
|
|
106
|
-
{
|
|
107
|
-
name: "custom-table",
|
|
108
|
-
description: "Dedicated custom table storage",
|
|
109
|
-
value: "custom-table",
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
name: "post-meta",
|
|
113
|
-
description: "Persist through post meta",
|
|
114
|
-
value: "post-meta",
|
|
115
|
-
},
|
|
116
|
-
],
|
|
117
|
-
visibleWhen: (values) =>
|
|
118
|
-
values.template === "persistence" || values.template === "compound",
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
kind: "select",
|
|
122
|
-
label: "Persistence policy",
|
|
123
|
-
name: "persistence-policy",
|
|
124
|
-
options: [
|
|
125
|
-
{
|
|
126
|
-
name: "authenticated",
|
|
127
|
-
description: "Authenticated write policy",
|
|
128
|
-
value: "authenticated",
|
|
129
|
-
},
|
|
130
|
-
{ name: "public", description: "Public token policy", value: "public" },
|
|
131
|
-
],
|
|
132
|
-
visibleWhen: (values) =>
|
|
133
|
-
values.template === "persistence" || values.template === "compound",
|
|
134
|
-
},
|
|
135
|
-
{ kind: "checkbox", label: "Skip dependency install", name: "no-install" },
|
|
136
|
-
{ kind: "checkbox", label: "Use defaults without prompts", name: "yes" },
|
|
137
|
-
{ kind: "checkbox", label: "Add wp-env preset", name: "with-wp-env" },
|
|
138
|
-
{
|
|
139
|
-
kind: "checkbox",
|
|
140
|
-
label: "Add test preset",
|
|
141
|
-
name: "with-test-preset",
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
kind: "checkbox",
|
|
145
|
-
label: "Add migration UI",
|
|
146
|
-
name: "with-migration-ui",
|
|
147
|
-
},
|
|
148
|
-
]}
|
|
149
|
-
initialValues={initialValues}
|
|
150
|
-
onCancel={() => runtime.exit()}
|
|
151
|
-
onSubmit={async (values) => {
|
|
152
|
-
try {
|
|
153
|
-
setErrorMessage(null);
|
|
154
|
-
await executeCreateCommand({
|
|
155
|
-
cwd,
|
|
156
|
-
flags: values,
|
|
157
|
-
interactive: true,
|
|
158
|
-
projectDir: values["project-dir"],
|
|
159
|
-
prompt: defaultPrompt,
|
|
160
|
-
});
|
|
161
|
-
runtime.exit();
|
|
162
|
-
} catch (error) {
|
|
163
|
-
setErrorMessage(error instanceof Error ? error.message : String(error));
|
|
164
|
-
}
|
|
165
|
-
}}
|
|
166
|
-
schema={createFlowSchema}
|
|
167
|
-
title="Create a wp-typia project"
|
|
168
|
-
/>
|
|
169
|
-
</>
|
|
177
|
+
<Form
|
|
178
|
+
initialValues={initialValues}
|
|
179
|
+
onCancel={handleCancel}
|
|
180
|
+
onSubmit={async (values) =>
|
|
181
|
+
handleSubmit(async () => {
|
|
182
|
+
const flags = sanitizeCreateSubmitValues(values);
|
|
183
|
+
await executeCreateCommand({
|
|
184
|
+
cwd,
|
|
185
|
+
flags,
|
|
186
|
+
interactive: true,
|
|
187
|
+
projectDir: values["project-dir"],
|
|
188
|
+
prompt: defaultPrompt,
|
|
189
|
+
});
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
schema={createFlowSchema}
|
|
193
|
+
title="Create a wp-typia project"
|
|
194
|
+
>
|
|
195
|
+
<CreateFlowFields />
|
|
196
|
+
</Form>
|
|
170
197
|
);
|
|
171
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
|
+
}
|