sitevision-cli 0.0.0
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/dist/app.d.ts +7 -0
- package/dist/app.js +180 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +95 -0
- package/dist/commands/build.d.ts +12 -0
- package/dist/commands/build.js +168 -0
- package/dist/commands/deploy.d.ts +17 -0
- package/dist/commands/deploy.js +162 -0
- package/dist/commands/dev.d.ts +15 -0
- package/dist/commands/dev.js +291 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +20 -0
- package/dist/commands/info.d.ts +2 -0
- package/dist/commands/info.js +66 -0
- package/dist/commands/setup-signing.d.ts +2 -0
- package/dist/commands/setup-signing.js +82 -0
- package/dist/commands/sign.d.ts +14 -0
- package/dist/commands/sign.js +103 -0
- package/dist/commands/types.d.ts +18 -0
- package/dist/commands/types.js +1 -0
- package/dist/components/DevPropertiesForm.d.ts +11 -0
- package/dist/components/DevPropertiesForm.js +87 -0
- package/dist/components/InfoScreen.d.ts +8 -0
- package/dist/components/InfoScreen.js +60 -0
- package/dist/components/MainMenu.d.ts +8 -0
- package/dist/components/MainMenu.js +138 -0
- package/dist/components/PasswordInput.d.ts +8 -0
- package/dist/components/PasswordInput.js +30 -0
- package/dist/components/ProcessOutput.d.ts +7 -0
- package/dist/components/ProcessOutput.js +32 -0
- package/dist/components/SetupFlow.d.ts +8 -0
- package/dist/components/SetupFlow.js +194 -0
- package/dist/components/SigningPropertiesForm.d.ts +8 -0
- package/dist/components/SigningPropertiesForm.js +49 -0
- package/dist/components/StatusIndicator.d.ts +9 -0
- package/dist/components/StatusIndicator.js +36 -0
- package/dist/components/TextInput.d.ts +11 -0
- package/dist/components/TextInput.js +37 -0
- package/dist/types/index.d.ts +250 -0
- package/dist/types/index.js +6 -0
- package/dist/utils/password-prompt.d.ts +4 -0
- package/dist/utils/password-prompt.js +45 -0
- package/dist/utils/process-runner.d.ts +30 -0
- package/dist/utils/process-runner.js +119 -0
- package/dist/utils/project-detection.d.ts +103 -0
- package/dist/utils/project-detection.js +287 -0
- package/dist/utils/sitevision-api.d.ts +56 -0
- package/dist/utils/sitevision-api.js +393 -0
- package/dist/utils/webpack-runner.d.ts +75 -0
- package/dist/utils/webpack-runner.js +313 -0
- package/dist/utils/zip.d.ts +64 -0
- package/dist/utils/zip.js +246 -0
- package/package.json +59 -0
- package/readme.md +196 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { TextInput } from './TextInput.js';
|
|
4
|
+
import { writeDevProperties } from '../utils/project-detection.js';
|
|
5
|
+
const STEPS = [
|
|
6
|
+
{ id: 'domain', label: 'Domain' },
|
|
7
|
+
{ id: 'siteName', label: 'Site Name' },
|
|
8
|
+
{ id: 'addonName', label: 'Addon Name' },
|
|
9
|
+
{ id: 'username', label: 'Username' },
|
|
10
|
+
{ id: 'password', label: 'Password' },
|
|
11
|
+
{ id: 'useHTTP', label: 'Use HTTP' },
|
|
12
|
+
];
|
|
13
|
+
export function DevPropertiesForm({ projectRoot, initialProperties, packageJson, onComplete, onCancel }) {
|
|
14
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
15
|
+
const [properties, setProperties] = useState(() => {
|
|
16
|
+
const defaults = {
|
|
17
|
+
domain: packageJson.developmentDomain || '',
|
|
18
|
+
addonName: packageJson.addonName || '',
|
|
19
|
+
siteName: packageJson.siteName || '',
|
|
20
|
+
};
|
|
21
|
+
return { ...defaults, ...initialProperties };
|
|
22
|
+
});
|
|
23
|
+
const currentStep = STEPS[stepIndex];
|
|
24
|
+
const handleNext = (key, value) => {
|
|
25
|
+
const newProperties = { ...properties, [key]: value };
|
|
26
|
+
setProperties(newProperties);
|
|
27
|
+
if (stepIndex < STEPS.length - 1) {
|
|
28
|
+
setStepIndex(stepIndex + 1);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Save and finish
|
|
32
|
+
writeDevProperties(projectRoot, newProperties);
|
|
33
|
+
onComplete();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const renderInput = () => {
|
|
37
|
+
switch (currentStep?.id) {
|
|
38
|
+
case 'domain':
|
|
39
|
+
return (React.createElement(TextInput, { key: "domain", label: "Development Domain (e.g. www.sitevision.se)", defaultValue: properties.domain, placeholder: "sitevision.se", onSubmit: (value) => handleNext('domain', value), onCancel: onCancel }));
|
|
40
|
+
case 'siteName':
|
|
41
|
+
return (React.createElement(TextInput, { key: "siteName", label: "Site Name (Root node name)", defaultValue: properties.siteName, onSubmit: (value) => handleNext('siteName', value), onCancel: onCancel }));
|
|
42
|
+
case 'addonName':
|
|
43
|
+
return (React.createElement(TextInput, { key: "addonName", label: "Addon Name", defaultValue: properties.addonName, onSubmit: (value) => handleNext('addonName', value), onCancel: onCancel }));
|
|
44
|
+
case 'username':
|
|
45
|
+
return (React.createElement(TextInput, { key: "username", label: "Username (usually your Sitevision Cloud email)", defaultValue: properties.username, onSubmit: (value) => handleNext('username', value), onCancel: onCancel }));
|
|
46
|
+
case 'password':
|
|
47
|
+
return (React.createElement(TextInput, { key: "password", label: "Password (Optional - leave empty to prompt on each run)", type: "password", defaultValue: properties.password, onSubmit: (value) => handleNext('password', value), onCancel: onCancel }));
|
|
48
|
+
case 'useHTTP':
|
|
49
|
+
return (React.createElement(BooleanInput, { key: "useHTTP", label: "Use HTTP for deployment? (y/n)", defaultValue: properties.useHTTPForDevDeploy, onSubmit: (value) => handleNext('useHTTPForDevDeploy', value) }));
|
|
50
|
+
default:
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
55
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
56
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Setup Development Properties"),
|
|
57
|
+
React.createElement(Text, null,
|
|
58
|
+
" Step ",
|
|
59
|
+
stepIndex + 1,
|
|
60
|
+
" of ",
|
|
61
|
+
STEPS.length,
|
|
62
|
+
": ",
|
|
63
|
+
currentStep?.label)),
|
|
64
|
+
React.createElement(Box, { marginBottom: 1 }, STEPS.map((s, i) => (React.createElement(Box, { key: s.id, marginRight: 1 },
|
|
65
|
+
React.createElement(Text, { color: i === stepIndex ? 'green' : i < stepIndex ? 'green' : 'gray' }, i < stepIndex ? '✓' : i === stepIndex ? '●' : '○'))))),
|
|
66
|
+
React.createElement(Box, { borderStyle: "single", borderColor: "gray", padding: 1 }, renderInput())));
|
|
67
|
+
}
|
|
68
|
+
function BooleanInput({ label, defaultValue, onSubmit }) {
|
|
69
|
+
useInput((input) => {
|
|
70
|
+
if (input === 'y' || input === 'Y') {
|
|
71
|
+
onSubmit(true);
|
|
72
|
+
}
|
|
73
|
+
else if (input === 'n' || input === 'N') {
|
|
74
|
+
onSubmit(false);
|
|
75
|
+
}
|
|
76
|
+
else if (input === '\r' && defaultValue !== undefined) {
|
|
77
|
+
onSubmit(defaultValue);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
81
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
82
|
+
React.createElement(Text, { bold: true, color: "cyan" }, label)),
|
|
83
|
+
React.createElement(Box, null,
|
|
84
|
+
React.createElement(Text, { dimColor: true },
|
|
85
|
+
"Press Y for Yes, N for No",
|
|
86
|
+
defaultValue !== undefined ? ` (Default: ${defaultValue ? 'Yes' : 'No'})` : ''))));
|
|
87
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { getAppType } from '../utils/project-detection.js';
|
|
4
|
+
export function InfoScreen({ project, onBack }) {
|
|
5
|
+
const appType = getAppType(project.manifest);
|
|
6
|
+
useInput((input, key) => {
|
|
7
|
+
if (key.escape || input === 'q' || key.return) {
|
|
8
|
+
onBack();
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
12
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
13
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision Project Information")),
|
|
14
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
15
|
+
React.createElement(Box, null,
|
|
16
|
+
React.createElement(Text, { bold: true }, "Name: "),
|
|
17
|
+
React.createElement(Text, null, project.manifest.name)),
|
|
18
|
+
React.createElement(Box, null,
|
|
19
|
+
React.createElement(Text, { bold: true }, "ID: "),
|
|
20
|
+
React.createElement(Text, null, project.manifest.id)),
|
|
21
|
+
React.createElement(Box, null,
|
|
22
|
+
React.createElement(Text, { bold: true }, "Version: "),
|
|
23
|
+
React.createElement(Text, null, project.manifest.version)),
|
|
24
|
+
React.createElement(Box, null,
|
|
25
|
+
React.createElement(Text, { bold: true }, "Type: "),
|
|
26
|
+
React.createElement(Text, { color: "green" }, project.manifest.type),
|
|
27
|
+
React.createElement(Text, { dimColor: true },
|
|
28
|
+
" (",
|
|
29
|
+
appType,
|
|
30
|
+
")")),
|
|
31
|
+
React.createElement(Box, null,
|
|
32
|
+
React.createElement(Text, { bold: true }, "Bundled: "),
|
|
33
|
+
React.createElement(Text, null, project.manifest.bundled ? 'Yes' : 'No'))),
|
|
34
|
+
project.hasDevProperties && project.devProperties && (React.createElement(React.Fragment, null,
|
|
35
|
+
React.createElement(Box, { marginTop: 1, marginBottom: 1 },
|
|
36
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Development Configuration")),
|
|
37
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
38
|
+
React.createElement(Box, null,
|
|
39
|
+
React.createElement(Text, { bold: true }, "Domain: "),
|
|
40
|
+
React.createElement(Text, null, project.devProperties.domain)),
|
|
41
|
+
React.createElement(Box, null,
|
|
42
|
+
React.createElement(Text, { bold: true }, "Site: "),
|
|
43
|
+
React.createElement(Text, null, project.devProperties.siteName)),
|
|
44
|
+
React.createElement(Box, null,
|
|
45
|
+
React.createElement(Text, { bold: true }, "Addon: "),
|
|
46
|
+
React.createElement(Text, null, project.devProperties.addonName)),
|
|
47
|
+
React.createElement(Box, null,
|
|
48
|
+
React.createElement(Text, { bold: true }, "Username: "),
|
|
49
|
+
React.createElement(Text, null, project.devProperties.username)),
|
|
50
|
+
React.createElement(Box, null,
|
|
51
|
+
React.createElement(Text, { bold: true }, "Use HTTP: "),
|
|
52
|
+
React.createElement(Text, null, project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No'))))),
|
|
53
|
+
!project.hasDevProperties && (React.createElement(Box, { marginTop: 1 },
|
|
54
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 No dev properties found. Run setup-dev-properties to configure."))),
|
|
55
|
+
React.createElement(Box, { marginTop: 1 },
|
|
56
|
+
React.createElement(Text, { bold: true }, "Project Root: "),
|
|
57
|
+
React.createElement(Text, { dimColor: true }, project.root)),
|
|
58
|
+
React.createElement(Box, { marginTop: 2 },
|
|
59
|
+
React.createElement(Text, { dimColor: true }, "Press Enter or ESC to return to menu"))));
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ProjectInfo } from '../utils/project-detection.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
project: ProjectInfo;
|
|
5
|
+
onSelect: (command: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function MainMenu({ project, onSelect }: Props): React.JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { getAppType } from '../utils/project-detection.js';
|
|
4
|
+
export function MainMenu({ project, onSelect }) {
|
|
5
|
+
const appType = getAppType(project.manifest);
|
|
6
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
7
|
+
const items = [
|
|
8
|
+
{
|
|
9
|
+
label: '🚀 Dev',
|
|
10
|
+
value: 'dev',
|
|
11
|
+
description: 'Start development server with watch mode',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: '🔐 Dev (Signed)',
|
|
15
|
+
value: 'dev-signed',
|
|
16
|
+
description: 'Development with automatic signing',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: '🔨 Build',
|
|
20
|
+
value: 'build',
|
|
21
|
+
description: 'Build the application for production',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: '✍️ Sign',
|
|
25
|
+
value: 'sign',
|
|
26
|
+
description: 'Sign the app for production deployment',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: '📦 Deploy',
|
|
30
|
+
value: 'deploy',
|
|
31
|
+
description: 'Deploy to development server',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: '🚢 Deploy (Force)',
|
|
35
|
+
value: 'deploy-force',
|
|
36
|
+
description: 'Force deploy (overwrite existing)',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: '🌍 Deploy Production',
|
|
40
|
+
value: 'deploy-production',
|
|
41
|
+
description: 'Deploy to production server',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'ℹ️ Info',
|
|
45
|
+
value: 'info',
|
|
46
|
+
description: 'Show project information',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: '❌ Exit',
|
|
50
|
+
value: 'exit',
|
|
51
|
+
description: 'Exit the CLI',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
useInput((input, key) => {
|
|
55
|
+
if (key.upArrow) {
|
|
56
|
+
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
|
|
57
|
+
}
|
|
58
|
+
if (key.downArrow) {
|
|
59
|
+
setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
|
|
60
|
+
}
|
|
61
|
+
if (key.return || input === '\r' || input === '\n') {
|
|
62
|
+
const selectedItem = items[selectedIndex];
|
|
63
|
+
if (!selectedItem)
|
|
64
|
+
return;
|
|
65
|
+
if (selectedItem.value === 'exit') {
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
onSelect(selectedItem.value);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
72
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
73
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision Project Information")),
|
|
74
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1 },
|
|
75
|
+
React.createElement(Box, null,
|
|
76
|
+
React.createElement(Text, { bold: true }, "Name: "),
|
|
77
|
+
React.createElement(Text, null, project.manifest.name)),
|
|
78
|
+
React.createElement(Box, null,
|
|
79
|
+
React.createElement(Text, { bold: true }, "ID: "),
|
|
80
|
+
React.createElement(Text, null, project.manifest.id)),
|
|
81
|
+
React.createElement(Box, null,
|
|
82
|
+
React.createElement(Text, { bold: true }, "Version: "),
|
|
83
|
+
React.createElement(Text, null, project.manifest.version)),
|
|
84
|
+
React.createElement(Box, null,
|
|
85
|
+
React.createElement(Text, { bold: true }, "Type: "),
|
|
86
|
+
React.createElement(Text, { color: "green" }, project.manifest.type),
|
|
87
|
+
React.createElement(Text, { dimColor: true },
|
|
88
|
+
" (",
|
|
89
|
+
appType,
|
|
90
|
+
")")),
|
|
91
|
+
React.createElement(Box, null,
|
|
92
|
+
React.createElement(Text, { bold: true }, "Bundled: "),
|
|
93
|
+
React.createElement(Text, null, project.manifest.bundled ? 'Yes' : 'No'))),
|
|
94
|
+
project.hasDevProperties && project.devProperties && (React.createElement(React.Fragment, null,
|
|
95
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
96
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Development Configuration")),
|
|
97
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1 },
|
|
98
|
+
React.createElement(Box, null,
|
|
99
|
+
React.createElement(Text, { bold: true }, "Domain: "),
|
|
100
|
+
React.createElement(Text, null, project.devProperties.domain)),
|
|
101
|
+
React.createElement(Box, null,
|
|
102
|
+
React.createElement(Text, { bold: true }, "Site: "),
|
|
103
|
+
React.createElement(Text, null, project.devProperties.siteName)),
|
|
104
|
+
React.createElement(Box, null,
|
|
105
|
+
React.createElement(Text, { bold: true }, "Addon: "),
|
|
106
|
+
React.createElement(Text, null, project.devProperties.addonName)),
|
|
107
|
+
React.createElement(Box, null,
|
|
108
|
+
React.createElement(Text, { bold: true }, "Username: "),
|
|
109
|
+
React.createElement(Text, null, project.devProperties.username)),
|
|
110
|
+
React.createElement(Box, null,
|
|
111
|
+
React.createElement(Text, { bold: true }, "Use HTTP: "),
|
|
112
|
+
React.createElement(Text, null, project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No'))))),
|
|
113
|
+
project.hasSigningProperties && project.devProperties && (React.createElement(React.Fragment, null,
|
|
114
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
115
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Signing Configuration")),
|
|
116
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1 },
|
|
117
|
+
React.createElement(Box, null,
|
|
118
|
+
React.createElement(Text, { bold: true }, "Signing User: "),
|
|
119
|
+
React.createElement(Text, null, project.devProperties.signingUsername)),
|
|
120
|
+
project.devProperties.certificateName && (React.createElement(Box, null,
|
|
121
|
+
React.createElement(Text, { bold: true }, "Certificate: "),
|
|
122
|
+
React.createElement(Text, null, project.devProperties.certificateName)))))),
|
|
123
|
+
!project.hasDevProperties && (React.createElement(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow" },
|
|
124
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 No dev properties found. Some commands may not work."))),
|
|
125
|
+
project.hasDevProperties && !project.hasSigningProperties && (React.createElement(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow" },
|
|
126
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 No signing credentials configured. Run svc setup-signing to configure."))),
|
|
127
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
128
|
+
React.createElement(Text, { dimColor: true }, "Select a command:")),
|
|
129
|
+
React.createElement(Box, { flexDirection: "column" }, items.map((item, index) => (React.createElement(Box, { key: item.value, marginLeft: 1 },
|
|
130
|
+
React.createElement(Text, { color: index === selectedIndex ? 'cyan' : undefined, bold: index === selectedIndex },
|
|
131
|
+
index === selectedIndex ? '▶ ' : ' ',
|
|
132
|
+
item.label,
|
|
133
|
+
item.description && (React.createElement(Text, { dimColor: true },
|
|
134
|
+
" - ",
|
|
135
|
+
item.description))))))),
|
|
136
|
+
React.createElement(Box, { marginTop: 1 },
|
|
137
|
+
React.createElement(Text, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select"))));
|
|
138
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
export function PasswordInput({ label = 'Enter Signing Password', onSubmit, onCancel }) {
|
|
4
|
+
const [password, setPassword] = useState('');
|
|
5
|
+
useInput((input, key) => {
|
|
6
|
+
if (key.return) {
|
|
7
|
+
onSubmit(password);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (key.escape) {
|
|
11
|
+
onCancel();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (key.delete || key.backspace) {
|
|
15
|
+
setPassword((prev) => prev.slice(0, -1));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
// Handle regular characters (including paste)
|
|
19
|
+
if (!key.ctrl && !key.meta) {
|
|
20
|
+
setPassword((prev) => prev + input);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
24
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
25
|
+
React.createElement(Text, { bold: true, color: "cyan" }, label)),
|
|
26
|
+
React.createElement(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1 },
|
|
27
|
+
React.createElement(Text, null, '*'.repeat(password.length))),
|
|
28
|
+
React.createElement(Box, { marginTop: 1 },
|
|
29
|
+
React.createElement(Text, { dimColor: true }, "Press Enter to submit, Esc to cancel"))));
|
|
30
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export function ProcessOutputComponent({ runner }) {
|
|
4
|
+
const [outputs, setOutputs] = useState([]);
|
|
5
|
+
const [isRunning, setIsRunning] = useState(true);
|
|
6
|
+
const [exitCode, setExitCode] = useState(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const handleOutput = (output) => {
|
|
9
|
+
setOutputs(prev => {
|
|
10
|
+
const newOutputs = [...prev, output];
|
|
11
|
+
if (newOutputs.length > 100) {
|
|
12
|
+
return newOutputs.slice(newOutputs.length - 100);
|
|
13
|
+
}
|
|
14
|
+
return newOutputs;
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
const handleExit = (code) => {
|
|
18
|
+
setIsRunning(false);
|
|
19
|
+
setExitCode(code);
|
|
20
|
+
};
|
|
21
|
+
runner.on('output', handleOutput);
|
|
22
|
+
runner.on('exit', handleExit);
|
|
23
|
+
return () => {
|
|
24
|
+
runner.off('output', handleOutput);
|
|
25
|
+
runner.off('exit', handleExit);
|
|
26
|
+
};
|
|
27
|
+
}, [runner]);
|
|
28
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
29
|
+
outputs.map((output, index) => (React.createElement(Text, { key: index, color: output.type === 'stderr' ? 'red' : undefined, dimColor: output.type === 'stderr' }, output.data))),
|
|
30
|
+
!isRunning && exitCode !== null && (React.createElement(Box, { marginTop: 1 },
|
|
31
|
+
React.createElement(Text, { color: exitCode === 0 ? 'green' : 'red' }, exitCode === 0 ? '✓ Success' : `✗ Failed with code ${exitCode}`)))));
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ProjectInfo } from '../utils/project-detection.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
project: ProjectInfo;
|
|
5
|
+
onComplete: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function SetupFlow({ project, onComplete }: Props): React.JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { getAppType } from '../utils/project-detection.js';
|
|
4
|
+
import { ProcessRunner } from '../utils/process-runner.js';
|
|
5
|
+
import { ProcessOutputComponent } from './ProcessOutput.js';
|
|
6
|
+
import { StatusIndicator } from './StatusIndicator.js';
|
|
7
|
+
import { DevPropertiesForm } from './DevPropertiesForm.js';
|
|
8
|
+
import { SigningPropertiesForm } from './SigningPropertiesForm.js';
|
|
9
|
+
export function SetupFlow({ project, onComplete }) {
|
|
10
|
+
const [step, setStep] = useState('check-node-modules');
|
|
11
|
+
const [runner, setRunner] = useState(null);
|
|
12
|
+
const [commandStatus, setCommandStatus] = useState('running');
|
|
13
|
+
const appType = getAppType(project.manifest);
|
|
14
|
+
// Auto-advance through checks
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (step === 'check-node-modules') {
|
|
17
|
+
if (project.hasNodeModules) {
|
|
18
|
+
setStep('check-dev-properties');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
setStep('confirm-npm-install');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (step === 'check-dev-properties') {
|
|
25
|
+
if (project.hasDevProperties) {
|
|
26
|
+
setStep('check-signing-properties');
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
setStep('confirm-dev-setup');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (step === 'check-signing-properties') {
|
|
33
|
+
if (project.hasSigningProperties) {
|
|
34
|
+
setStep('show-info');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
setStep('confirm-signing-setup');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (step === 'show-info') {
|
|
41
|
+
// Auto-advance to menu after displaying info
|
|
42
|
+
onComplete();
|
|
43
|
+
}
|
|
44
|
+
}, [step, project, onComplete]); // Removed specific props from dependency array to allow re-check after updates
|
|
45
|
+
useInput((input) => {
|
|
46
|
+
if (step === 'confirm-npm-install') {
|
|
47
|
+
if (input === 'y' || input === 'Y') {
|
|
48
|
+
setStep('running-npm-install');
|
|
49
|
+
setCommandStatus('running');
|
|
50
|
+
// npm install is a native command, not a script
|
|
51
|
+
const newRunner = new ProcessRunner('npm', ['install'], project.root);
|
|
52
|
+
newRunner.on('exit', (code) => {
|
|
53
|
+
if (code === 0) {
|
|
54
|
+
setCommandStatus('success');
|
|
55
|
+
// Force project info refresh would be ideal here
|
|
56
|
+
// For now, assume success and move on
|
|
57
|
+
setTimeout(() => setStep('check-dev-properties'), 1000);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
setCommandStatus('error');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
setRunner(newRunner);
|
|
64
|
+
newRunner.run().catch(() => setCommandStatus('error'));
|
|
65
|
+
}
|
|
66
|
+
else if (input === 'n' || input === 'N') {
|
|
67
|
+
setStep('check-dev-properties');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (step === 'confirm-dev-setup') {
|
|
71
|
+
if (input === 'y' || input === 'Y') {
|
|
72
|
+
setStep('setup-dev-properties');
|
|
73
|
+
}
|
|
74
|
+
else if (input === 'n' || input === 'N') {
|
|
75
|
+
setStep('check-signing-properties');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (step === 'confirm-signing-setup') {
|
|
79
|
+
if (input === 'y' || input === 'Y') {
|
|
80
|
+
setStep('setup-signing-properties');
|
|
81
|
+
}
|
|
82
|
+
else if (input === 'n' || input === 'N') {
|
|
83
|
+
setStep('show-info');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// Setup Dev Properties Form
|
|
88
|
+
if (step === 'setup-dev-properties') {
|
|
89
|
+
return (React.createElement(DevPropertiesForm, { projectRoot: project.root, initialProperties: project.devProperties, packageJson: project.packageJson, onComplete: () => {
|
|
90
|
+
// Manually update project state locally if possible, or just proceed
|
|
91
|
+
// Since we can't easily update 'project' prop from here without reloading,
|
|
92
|
+
// we just move to next step. The file is written.
|
|
93
|
+
project.hasDevProperties = true; // Optimization/Hack to pass check
|
|
94
|
+
setStep('check-signing-properties');
|
|
95
|
+
}, onCancel: () => setStep('check-signing-properties') }));
|
|
96
|
+
}
|
|
97
|
+
// Setup Signing Properties Form
|
|
98
|
+
if (step === 'setup-signing-properties') {
|
|
99
|
+
return (React.createElement(SigningPropertiesForm, { projectRoot: project.root, onComplete: () => {
|
|
100
|
+
project.hasSigningProperties = true; // Optimization/Hack
|
|
101
|
+
setStep('show-info');
|
|
102
|
+
}, onCancel: () => setStep('show-info') }));
|
|
103
|
+
}
|
|
104
|
+
// Running npm install
|
|
105
|
+
if (step === 'running-npm-install') {
|
|
106
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
107
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
108
|
+
React.createElement(StatusIndicator, { status: commandStatus, label: "Installing dependencies" })),
|
|
109
|
+
runner && React.createElement(ProcessOutputComponent, { runner: runner })));
|
|
110
|
+
}
|
|
111
|
+
// Confirm npm install
|
|
112
|
+
if (step === 'confirm-npm-install') {
|
|
113
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
114
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
115
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision CLI")),
|
|
116
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
117
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 node_modules not found")),
|
|
118
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
119
|
+
React.createElement(Text, null, "Would you like to run npm install? (y/n)"))));
|
|
120
|
+
}
|
|
121
|
+
// Confirm dev setup
|
|
122
|
+
if (step === 'confirm-dev-setup') {
|
|
123
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
124
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
125
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision CLI")),
|
|
126
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
127
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 dev properties not configured")),
|
|
128
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
129
|
+
React.createElement(Text, null, "Would you like to set up dev properties? (y/n)"))));
|
|
130
|
+
}
|
|
131
|
+
// Confirm signing setup
|
|
132
|
+
if (step === 'confirm-signing-setup') {
|
|
133
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
134
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
135
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision CLI")),
|
|
136
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
137
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 signing credentials not configured")),
|
|
138
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
139
|
+
React.createElement(Text, null, "Signing credentials are required for signing apps on developer.sitevision.se")),
|
|
140
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
141
|
+
React.createElement(Text, null, "Would you like to set up signing credentials? (y/n)"))));
|
|
142
|
+
}
|
|
143
|
+
// Show info
|
|
144
|
+
if (step === 'show-info') {
|
|
145
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
146
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
147
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Sitevision Project Information")),
|
|
148
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
149
|
+
React.createElement(Box, null,
|
|
150
|
+
React.createElement(Text, { bold: true }, "Name: "),
|
|
151
|
+
React.createElement(Text, null, project.manifest.name)),
|
|
152
|
+
React.createElement(Box, null,
|
|
153
|
+
React.createElement(Text, { bold: true }, "ID: "),
|
|
154
|
+
React.createElement(Text, null, project.manifest.id)),
|
|
155
|
+
React.createElement(Box, null,
|
|
156
|
+
React.createElement(Text, { bold: true }, "Version: "),
|
|
157
|
+
React.createElement(Text, null, project.manifest.version)),
|
|
158
|
+
React.createElement(Box, null,
|
|
159
|
+
React.createElement(Text, { bold: true }, "Type: "),
|
|
160
|
+
React.createElement(Text, { color: "green" }, project.manifest.type),
|
|
161
|
+
React.createElement(Text, { dimColor: true },
|
|
162
|
+
" (",
|
|
163
|
+
appType,
|
|
164
|
+
")")),
|
|
165
|
+
React.createElement(Box, null,
|
|
166
|
+
React.createElement(Text, { bold: true }, "Bundled: "),
|
|
167
|
+
React.createElement(Text, null, project.manifest.bundled ? 'Yes' : 'No'))),
|
|
168
|
+
project.hasDevProperties && project.devProperties && (React.createElement(React.Fragment, null,
|
|
169
|
+
React.createElement(Box, { marginTop: 1, marginBottom: 1 },
|
|
170
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Development Configuration")),
|
|
171
|
+
React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
172
|
+
React.createElement(Box, null,
|
|
173
|
+
React.createElement(Text, { bold: true }, "Domain: "),
|
|
174
|
+
React.createElement(Text, null, project.devProperties.domain)),
|
|
175
|
+
React.createElement(Box, null,
|
|
176
|
+
React.createElement(Text, { bold: true }, "Site: "),
|
|
177
|
+
React.createElement(Text, null, project.devProperties.siteName)),
|
|
178
|
+
React.createElement(Box, null,
|
|
179
|
+
React.createElement(Text, { bold: true }, "Addon: "),
|
|
180
|
+
React.createElement(Text, null, project.devProperties.addonName)),
|
|
181
|
+
React.createElement(Box, null,
|
|
182
|
+
React.createElement(Text, { bold: true }, "Username: "),
|
|
183
|
+
React.createElement(Text, null, project.devProperties.username)),
|
|
184
|
+
React.createElement(Box, null,
|
|
185
|
+
React.createElement(Text, { bold: true }, "Use HTTP: "),
|
|
186
|
+
React.createElement(Text, null, project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No'))))),
|
|
187
|
+
!project.hasDevProperties && (React.createElement(Box, { marginTop: 1 },
|
|
188
|
+
React.createElement(Text, { color: "yellow" }, "\u26A0 No dev properties configured"))),
|
|
189
|
+
React.createElement(Box, { marginTop: 1 },
|
|
190
|
+
React.createElement(Text, { bold: true }, "Project Root: "),
|
|
191
|
+
React.createElement(Text, { dimColor: true }, project.root))));
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { TextInput } from './TextInput.js';
|
|
4
|
+
import { writeDevProperties, readDevProperties } from '../utils/project-detection.js';
|
|
5
|
+
const STEPS = [
|
|
6
|
+
{ id: 'username', label: 'Signing Username' },
|
|
7
|
+
{ id: 'certificate', label: 'Certificate Name' },
|
|
8
|
+
];
|
|
9
|
+
export function SigningPropertiesForm({ projectRoot, onComplete, onCancel }) {
|
|
10
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
11
|
+
// Read existing properties to preserve other fields
|
|
12
|
+
const [properties, setProperties] = useState(() => readDevProperties(projectRoot) || {});
|
|
13
|
+
const currentStep = STEPS[stepIndex];
|
|
14
|
+
const handleNext = (key, value) => {
|
|
15
|
+
const newProperties = { ...properties, [key]: value };
|
|
16
|
+
setProperties(newProperties);
|
|
17
|
+
if (stepIndex < STEPS.length - 1) {
|
|
18
|
+
setStepIndex(stepIndex + 1);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
// Save and finish
|
|
22
|
+
writeDevProperties(projectRoot, newProperties);
|
|
23
|
+
onComplete();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const renderInput = () => {
|
|
27
|
+
switch (currentStep?.id) {
|
|
28
|
+
case 'username':
|
|
29
|
+
return (React.createElement(TextInput, { key: "username", label: "Signing Username (developer.sitevision.se)", defaultValue: properties.signingUsername, onSubmit: (value) => handleNext('signingUsername', value), onCancel: onCancel }));
|
|
30
|
+
case 'certificate':
|
|
31
|
+
return (React.createElement(TextInput, { key: "certificate", label: "Certificate Name (Optional)", defaultValue: properties.certificateName, placeholder: "Leave empty for default", onSubmit: (value) => handleNext('certificateName', value), onCancel: onCancel }));
|
|
32
|
+
default:
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
37
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
38
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Setup Signing Properties"),
|
|
39
|
+
React.createElement(Text, null,
|
|
40
|
+
" Step ",
|
|
41
|
+
stepIndex + 1,
|
|
42
|
+
" of ",
|
|
43
|
+
STEPS.length,
|
|
44
|
+
": ",
|
|
45
|
+
currentStep?.label)),
|
|
46
|
+
React.createElement(Box, { marginBottom: 1 }, STEPS.map((s, i) => (React.createElement(Box, { key: s.id, marginRight: 1 },
|
|
47
|
+
React.createElement(Text, { color: i === stepIndex ? 'green' : i < stepIndex ? 'green' : 'gray' }, i < stepIndex ? '✓' : i === stepIndex ? '●' : '○'))))),
|
|
48
|
+
React.createElement(Box, { borderStyle: "single", borderColor: "gray", padding: 1 }, renderInput())));
|
|
49
|
+
}
|