sitevision-cli 1.0.0-beta.32 → 1.0.0-beta.33

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE +21 -0
  3. package/dist/cli.js +56 -35
  4. package/dist/commands/TaskScreen.js +32 -0
  5. package/dist/commands/build.js +6 -185
  6. package/dist/commands/deploy.js +85 -238
  7. package/dist/commands/dev.js +34 -37
  8. package/dist/commands/info.js +1 -1
  9. package/dist/commands/setup-signing.js +43 -28
  10. package/dist/commands/sign.js +9 -86
  11. package/dist/commands/use-finish.js +17 -0
  12. package/dist/commands/watch.js +0 -8
  13. package/dist/components/AuthLoginScreen.js +27 -12
  14. package/dist/shell/ConfigForm.js +90 -29
  15. package/dist/shell/Frame.js +4 -1
  16. package/dist/shell/Help.js +2 -1
  17. package/dist/shell/Settings.js +59 -6
  18. package/dist/shell/Shell.js +124 -41
  19. package/dist/shell/Tabs.js +8 -2
  20. package/dist/shell/actions.js +0 -0
  21. package/dist/utils/config.js +46 -3
  22. package/dist/utils/create-app.js +9 -2
  23. package/dist/utils/i18n.js +36 -13
  24. package/dist/utils/jsonc.js +2 -1
  25. package/dist/utils/keychain.js +44 -4
  26. package/dist/utils/oauth2-auth.js +35 -7
  27. package/dist/utils/password-prompt.js +23 -3
  28. package/dist/utils/process-runner.js +4 -2
  29. package/dist/utils/project-detection.js +84 -33
  30. package/dist/utils/session-cookie-auth.js +5 -4
  31. package/dist/utils/sitevision-api.js +21 -14
  32. package/dist/utils/sitevision-scripts-runner.js +32 -15
  33. package/dist/utils/tasks.js +130 -64
  34. package/dist/utils/version-check.js +3 -3
  35. package/dist/utils/webpack-runner.js +6 -5
  36. package/dist/utils/workspace.js +41 -3
  37. package/dist/utils/zip.js +18 -19
  38. package/package.json +24 -3
  39. package/readme.md +11 -4
  40. package/dist/app.d.ts +0 -6
  41. package/dist/app.js +0 -321
  42. package/dist/cli.d.ts +0 -2
  43. package/dist/commands/build.d.ts +0 -12
  44. package/dist/commands/deploy.d.ts +0 -17
  45. package/dist/commands/dev.d.ts +0 -14
  46. package/dist/commands/index.d.ts +0 -4
  47. package/dist/commands/info.d.ts +0 -2
  48. package/dist/commands/setup-signing.d.ts +0 -2
  49. package/dist/commands/sign.d.ts +0 -14
  50. package/dist/commands/types.d.ts +0 -18
  51. package/dist/commands/watch.d.ts +0 -2
  52. package/dist/components/AnimatedLogo.d.ts +0 -10
  53. package/dist/components/AuthLoginScreen.d.ts +0 -21
  54. package/dist/components/DevPropertiesForm.d.ts +0 -11
  55. package/dist/components/DevPropertiesForm.js +0 -246
  56. package/dist/components/InfoScreen.d.ts +0 -7
  57. package/dist/components/InfoScreen.js +0 -14
  58. package/dist/components/KeychainPasswordChoice.d.ts +0 -8
  59. package/dist/components/KeychainPasswordChoice.js +0 -30
  60. package/dist/components/MainMenu.d.ts +0 -7
  61. package/dist/components/MainMenu.js +0 -88
  62. package/dist/components/PasswordInput.d.ts +0 -10
  63. package/dist/components/ProcessOutput.d.ts +0 -6
  64. package/dist/components/ProcessOutput.js +0 -30
  65. package/dist/components/SetupFlow.d.ts +0 -8
  66. package/dist/components/SetupFlow.js +0 -233
  67. package/dist/components/SigningPropertiesForm.d.ts +0 -7
  68. package/dist/components/SigningPropertiesForm.js +0 -38
  69. package/dist/components/StatusIndicator.d.ts +0 -8
  70. package/dist/components/TextInput.d.ts +0 -10
  71. package/dist/components/TextInput.js +0 -29
  72. package/dist/components/WelcomeScreen.d.ts +0 -14
  73. package/dist/scaffold/inquirer-stub.d.ts +0 -6
  74. package/dist/scaffold/register.d.ts +0 -1
  75. package/dist/shell/AddonPicker.d.ts +0 -14
  76. package/dist/shell/Changelog.d.ts +0 -10
  77. package/dist/shell/CommandPalette.d.ts +0 -12
  78. package/dist/shell/ConfigForm.d.ts +0 -42
  79. package/dist/shell/Frame.d.ts +0 -59
  80. package/dist/shell/Help.d.ts +0 -9
  81. package/dist/shell/Popover.d.ts +0 -12
  82. package/dist/shell/Settings.d.ts +0 -6
  83. package/dist/shell/Shell.d.ts +0 -11
  84. package/dist/shell/Tabs.d.ts +0 -36
  85. package/dist/shell/actions.d.ts +0 -49
  86. package/dist/types/index.d.ts +0 -295
  87. package/dist/utils/branding.d.ts +0 -28
  88. package/dist/utils/config.d.ts +0 -27
  89. package/dist/utils/create-app.d.ts +0 -53
  90. package/dist/utils/environments.d.ts +0 -20
  91. package/dist/utils/i18n.d.ts +0 -12
  92. package/dist/utils/jsonc.d.ts +0 -19
  93. package/dist/utils/keychain.d.ts +0 -15
  94. package/dist/utils/oauth2-auth.d.ts +0 -64
  95. package/dist/utils/password-prompt.d.ts +0 -14
  96. package/dist/utils/process-runner.d.ts +0 -32
  97. package/dist/utils/project-detection.d.ts +0 -210
  98. package/dist/utils/session-cookie-auth.d.ts +0 -35
  99. package/dist/utils/signing-password.d.ts +0 -12
  100. package/dist/utils/signing-step.d.ts +0 -15
  101. package/dist/utils/signing-step.js +0 -16
  102. package/dist/utils/sitevision-api.d.ts +0 -155
  103. package/dist/utils/sitevision-scripts-runner.d.ts +0 -84
  104. package/dist/utils/tasks.d.ts +0 -61
  105. package/dist/utils/version-check.d.ts +0 -9
  106. package/dist/utils/webpack-runner.d.ts +0 -76
  107. package/dist/utils/workspace.d.ts +0 -17
  108. package/dist/utils/zip.d.ts +0 -71
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.33
4
+
5
+ - **Breaking:** `--token` and `--cookie` are removed, since credentials on the
6
+ command line end up in shell history. Use `SITEVISION_ACCESS_TOKEN` or
7
+ `SITEVISION_SESSION_COOKIE` instead.
8
+ - **Breaking:** an unknown flag is now an error (exit code 2), so a misspelt
9
+ `--production` never turns into a dev deploy.
10
+ - `svc build`, `svc sign` and `svc deploy` work without a terminal. They print
11
+ their log line by line and exit on their own with a proper exit code: 0 on
12
+ success, 1 on failure, 130 when a prompt is cancelled. `svc dev` and
13
+ `svc watch` no longer crash when stdin is a pipe.
14
+ - New flags: `--no-zip` for `build`, and the short forms `-s`, `-f`, `-p` and
15
+ `-a`.
16
+ - Global settings in `~/.config/sitevision-cli/config.json`: a default signing
17
+ user and certificate for every project that does not set its own, and
18
+ switches for the update check and the addon name warning. Edit them with `,`
19
+ in the shell, which now shows a description of each setting and opens from
20
+ anywhere, or run `svc setup-signing --global`. A file that does not parse is
21
+ reported and left alone.
22
+ - The Config tab marks the addon name with `≠ manifest` when it matches none
23
+ of the manifest's names. Nothing is changed for you; the two are different
24
+ things and `svc` only points the difference out.
25
+ - The manifest's name and description always have a Swedish row in the Config
26
+ tab. Filling it in on a plain name keeps the old text as the English one.
27
+ - In a workspace, an environment's addon name is stored per app. Setting it
28
+ for one app no longer leaks into the others.
29
+ - `svc` names the missing setting when the deploy config is incomplete,
30
+ instead of posting to "undefined".
31
+ - A production deploy with `--activate` fails if the activation did not
32
+ happen, and the confirmation names the addon it deploys to.
33
+ - OAuth2 requires https and a token endpoint on the site's own domain. A
34
+ refresh token is only thrown away when the site rejects it, not on a network
35
+ error. An expired session cookie is removed so the next deploy asks for a
36
+ new login.
37
+ - A plaintext password in `.dev_properties.json` is moved to the keychain when
38
+ the file is saved, and `svc` says so when the keychain cannot store a secret.
39
+ `SVC_NO_KEYCHAIN=1` turns the keychain off for CI.
40
+ - `svc dev` with a project's own webpack config no longer deploys a half-built
41
+ zip when several files change at once.
42
+ - Builds work when dependencies are hoisted to the workspace root, a
43
+ `manifest.json` in the project root is picked up, file names with å, ä and ö
44
+ survive in the zip, and `SITEVISION_APP_ID_PREFIX`/`_SUFFIX` apply to build,
45
+ sign and deploy alike.
46
+ - A broken app in a workspace is skipped with a message instead of stopping
47
+ the shell. The shell asks before quitting during app creation, the PROD
48
+ badge survives a narrow terminal, Esc cancels a confirm, and Ctrl-modified
49
+ keys never fire a shortcut.
50
+ - The update check is skipped without a terminal and when `CI` is set.
51
+ - Updated user guides in English and Swedish.
52
+
3
53
  ## 1.0.0-beta.32
4
54
 
5
55
  - Sitevision CLI can now create new apps. **New app** in the command palette
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rasmus Söderström
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import { requireProject, detectProject, migrateLegacyPassword, } from './utils/p
13
13
  import { discoverApps } from './utils/workspace.js';
14
14
  import { promptYesNo } from './utils/password-prompt.js';
15
15
  import { checkForUpdate } from './utils/version-check.js';
16
- import { isFirstRun, markFirstRunComplete, getLastSeenVersion, setLastSeenVersion, getSettings, } from './utils/config.js';
16
+ import { isFirstRun, markFirstRunComplete, getLastSeenVersion, setLastSeenVersion, getSettings, configProblem, settingsFile, } from './utils/config.js';
17
17
  import { setLanguage } from './utils/i18n.js';
18
18
  import { WelcomeScreen } from './components/WelcomeScreen.js';
19
19
  import { AnimatedLogo } from './components/AnimatedLogo.js';
@@ -31,11 +31,21 @@ const cli = meow(`
31
31
  sign Sign the app for production deployment
32
32
  deploy Deploy the application
33
33
  info Show project information
34
+ setup-signing Store the signing username and certificate
34
35
 
35
36
  Options
36
- --minimal Shell: compact layout for small terminals
37
- --help Show this help message
38
- --version Show version number
37
+ --signed, -s dev/watch: sign after each build
38
+ --force, -f deploy: overwrite the existing addon version
39
+ --production, -p deploy: upload the signed zip to production
40
+ --activate, -a deploy: activate after a production deploy
41
+ --no-zip build: skip the zip archive
42
+ --global setup-signing: save for every project on this machine
43
+ --minimal Shell: compact layout for small terminals
44
+ --help Show this help message
45
+ --version Show version number
46
+
47
+ Credentials for CI come from the environment: SITEVISION_DEPLOY_PASSWORD,
48
+ SITEVISION_ACCESS_TOKEN or SITEVISION_SESSION_COOKIE.
39
49
 
40
50
  Examples
41
51
  $ svc # Shell: run inside an app, or at the repo root
@@ -50,9 +60,20 @@ const cli = meow(`
50
60
  $ svc info
51
61
  `, {
52
62
  importMeta: import.meta,
63
+ allowUnknownFlags: false,
53
64
  flags: {
54
65
  signed: {
55
66
  type: 'boolean',
67
+ shortFlag: 's',
68
+ default: false,
69
+ },
70
+ zip: {
71
+ type: 'boolean',
72
+ default: true,
73
+ },
74
+ activate: {
75
+ type: 'boolean',
76
+ shortFlag: 'a',
56
77
  default: false,
57
78
  },
58
79
  force: {
@@ -65,16 +86,14 @@ const cli = meow(`
65
86
  shortFlag: 'p',
66
87
  default: false,
67
88
  },
68
- token: {
69
- type: 'string',
70
- },
71
- cookie: {
72
- type: 'string',
73
- },
74
89
  minimal: {
75
90
  type: 'boolean',
76
91
  default: false,
77
92
  },
93
+ global: {
94
+ type: 'boolean',
95
+ default: false,
96
+ },
78
97
  },
79
98
  });
80
99
  const [commandName, ...args] = cli.input;
@@ -145,10 +164,19 @@ async function playIntro(art) {
145
164
  // Run the full-screen shell on the alternate screen buffer so the scrollback
146
165
  // is untouched, and restore it on exit. The animated wordmark plays first,
147
166
  // inside the same buffer, when the terminal is wide enough for it.
148
- async function runShell(apps, workspaceRoot, updatedFrom) {
167
+ async function runShell(apps, workspaceRoot, updatedFrom, skipped = []) {
149
168
  process.stdout.write('\x1b[?1049h\x1b[H');
150
169
  // Also leave the alternate screen when a signal exits past the finally.
151
170
  process.once('exit', () => process.stdout.write('\x1b[?1049l'));
171
+ // A crash outside React would print into the alternate screen and vanish
172
+ // with it: leave it first, then report.
173
+ const crash = (error) => {
174
+ process.stdout.write('\x1b[?1049l');
175
+ console.error('svc crashed:', error);
176
+ shutdown(1);
177
+ };
178
+ process.once('uncaughtException', crash);
179
+ process.once('unhandledRejection', crash);
152
180
  try {
153
181
  const art = process.stdin.isTTY && settings.introAnimation && !cli.flags.minimal
154
182
  ? pickIntroArt(process.stdout.columns ?? 0)
@@ -161,7 +189,7 @@ async function runShell(apps, workspaceRoot, updatedFrom) {
161
189
  // the terminal to itself, and starts the shell again.
162
190
  for (;;) {
163
191
  let job;
164
- const app = render(_jsx(Shell, { apps: apps, workspaceRoot: workspaceRoot, version: pkg.version, minimal: cli.flags.minimal, updatedFrom: updatedFrom, handover: next => {
192
+ const app = render(_jsx(Shell, { apps: apps, workspaceRoot: workspaceRoot, version: pkg.version, minimal: cli.flags.minimal, updatedFrom: updatedFrom, skipped: skipped, handover: next => {
165
193
  job = next;
166
194
  app.unmount();
167
195
  } }));
@@ -172,8 +200,10 @@ async function runShell(apps, workspaceRoot, updatedFrom) {
172
200
  process.stdout.write('\x1b[?1049l');
173
201
  // eslint-disable-next-line no-await-in-loop
174
202
  await job();
175
- if (workspaceRoot)
176
- apps = discoverApps(workspaceRoot);
203
+ if (workspaceRoot) {
204
+ skipped = [];
205
+ apps = discoverApps(workspaceRoot, skipped);
206
+ }
177
207
  updatedFrom = undefined;
178
208
  process.stdout.write('\x1b[?1049h\x1b[2J\x1b[H');
179
209
  }
@@ -209,8 +239,13 @@ async function main() {
209
239
  }
210
240
  // Record the current version so the banner shows once per upgrade.
211
241
  setLastSeenVersion(pkg.version);
242
+ if (configProblem()) {
243
+ console.log(`\x1b[33m ${settingsFile()} does not parse and is ignored: ${configProblem()}\x1b[0m`);
244
+ }
212
245
  // Check npm for a newer published release.
213
- const latestVersion = await checkForUpdate(pkg.name, pkg.version);
246
+ const latestVersion = settings.updateCheck && process.stdout.isTTY && !process.env['CI']
247
+ ? await checkForUpdate(pkg.name, pkg.version)
248
+ : null;
214
249
  if (latestVersion) {
215
250
  console.log(`\x1b[33m ↑ update available: ${pkg.version} → ${latestVersion} (run: npm i -g ${pkg.name})\x1b[0m`);
216
251
  }
@@ -226,11 +261,13 @@ async function main() {
226
261
  fail(error.message, 'Fix the manifest and try again');
227
262
  }
228
263
  if (!project) {
229
- const apps = discoverApps(process.cwd());
264
+ const skipped = [];
265
+ const apps = discoverApps(process.cwd(), skipped);
230
266
  if (apps.length === 0) {
231
- fail('No Sitevision apps found here.', 'Run svc inside an app directory (manifest.json) or at the root of a repo that contains apps.');
267
+ fail('No Sitevision apps found here.', skipped[0] ??
268
+ 'Run svc inside an app directory (manifest.json) or at the root of a repo that contains apps.');
232
269
  }
233
- await runShell(apps, process.cwd(), isUpdate ? lastSeen : undefined);
270
+ await runShell(apps, process.cwd(), isUpdate ? lastSeen : undefined, skipped);
234
271
  shutdown();
235
272
  return;
236
273
  }
@@ -257,22 +294,6 @@ async function main() {
257
294
  return fail(error.message, "Make sure you're in a Sitevision project directory");
258
295
  }
259
296
  })();
260
- // --token / --cookie override the resolved bearer token / session cookie for
261
- // this run (manual / CI path, alongside SITEVISION_ACCESS_TOKEN and
262
- // SITEVISION_SESSION_COOKIE).
263
- if (cli.flags.token || cli.flags.cookie) {
264
- if (project.devProperties) {
265
- if (cli.flags.token) {
266
- project.devProperties.accessToken = cli.flags.token;
267
- }
268
- if (cli.flags.cookie) {
269
- project.devProperties.sessionCookie = cli.flags.cookie;
270
- }
271
- }
272
- else {
273
- console.log('\x1b[33m--token/--cookie needs a .dev_properties.json (domain, site, addon) to deploy against.\x1b[0m');
274
- }
275
- }
276
297
  // Get the command
277
298
  const command = getCommand(commandName);
278
299
  if (!command) {
@@ -290,7 +311,7 @@ async function main() {
290
311
  console.log('\x1b[32m✓ Password moved to keychain.\x1b[0m\n');
291
312
  }
292
313
  else {
293
- console.log('\x1b[31mCould not access keychain; leaving the file unchanged.\x1b[0m\n');
314
+ console.log('\x1b[31mCould not move the password; the file still holds it.\x1b[0m\n');
294
315
  }
295
316
  }
296
317
  }
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { styleText } from 'node:util';
4
+ import { Box, useStdout } from 'ink';
5
+ import { StatusIndicator } from '../components/StatusIndicator.js';
6
+ import { useTasks } from '../utils/tasks.js';
7
+ import { useFinish } from './use-finish.js';
8
+ const LEVEL_STYLE = { info: 'reset', ok: 'green', warn: 'yellow', error: 'red' };
9
+ /**
10
+ * A one-shot command: the task's log printed above the status as it comes (a
11
+ * terminal keeps it in scrollback, CI keeps all of it), then exit with its result.
12
+ */
13
+ export function TaskScreen({ task, stay = false, children }) {
14
+ const { write } = useStdout();
15
+ const live = useTasks().find(t => t.id === task.id) ?? task;
16
+ const result = live.status === 'running'
17
+ ? undefined
18
+ : live.status === 'success'
19
+ ? 'success'
20
+ : 'error';
21
+ // ponytail: counts lines, so past the task's 2000-line cap (where old lines
22
+ // are dropped) some would be skipped. No one-shot command logs that much.
23
+ const printed = useRef(0);
24
+ useEffect(() => {
25
+ for (const line of live.lines.slice(printed.current)) {
26
+ write(`${styleText('dim', line.tag.padEnd(3))} ${styleText(LEVEL_STYLE[line.level], line.text)}\n`);
27
+ }
28
+ printed.current = live.lines.length;
29
+ });
30
+ useFinish(result, stay && result === 'error');
31
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [_jsx(StatusIndicator, { status: result ?? 'running', label: live.phase, message: `${live.label} ${live.appName}` }), children] }));
32
+ }
@@ -1,192 +1,13 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
3
- import { render, Box, Text, useInput } from 'ink';
4
- import { StatusIndicator } from '../components/StatusIndicator.js';
5
- import { WebpackRunner, hasLocalWebpackConfig } from '../utils/webpack-runner.js';
6
- import { hasSitevisionScripts, runSitevisionScriptsBuild, getDelegatedZipPath, checkSitevisionScriptsCompatibility, } from '../utils/sitevision-scripts-runner.js';
7
- import { copyStaticToBuild, copySrcToBuild, cleanBuild, formatFileSize, createBuildZip, getZipSize, zipExists, } from '../utils/zip.js';
8
- import { isBundledApp, getAppType, getFullAppId, } from '../utils/project-detection.js';
9
- export function BuildScreen({ projectRoot, manifest, createZip = true, onBack, }) {
10
- const [state, setState] = React.useState({
11
- status: 'cleaning',
12
- message: 'Cleaning build directory...',
13
- });
14
- useInput((input, key) => {
15
- if (onBack &&
16
- (key.escape || input === 'q') &&
17
- (state.status === 'success' || state.status === 'error')) {
18
- onBack();
19
- }
20
- });
21
- const isBundled = isBundledApp(manifest);
22
- React.useEffect(() => {
23
- async function runBuild() {
24
- try {
25
- // Step 1: Clean build directory
26
- setState({ status: 'cleaning', message: 'Cleaning build directory...' });
27
- cleanBuild(projectRoot);
28
- // Step 2: Build or copy files
29
- if (isBundled && !hasLocalWebpackConfig(projectRoot)) {
30
- // No project-local webpack config: delegate the whole build to
31
- // @sitevision/sitevision-scripts, which compiles + zips to
32
- // dist/<appId>.zip (the path the CLI's sign/deploy already use).
33
- if (!hasSitevisionScripts(projectRoot)) {
34
- setState({
35
- status: 'error',
36
- error: 'No webpack.config.js found and @sitevision/sitevision-scripts is not installed. Run npm install.',
37
- });
38
- return;
39
- }
40
- // Warn if the installed sitevision-scripts is outside the range
41
- // the delegated build was validated against.
42
- const warning = checkSitevisionScriptsCompatibility(projectRoot).warning;
43
- setState({
44
- status: 'building',
45
- message: 'Building via sitevision-scripts...',
46
- warning,
47
- });
48
- const sitevisionResult = await runSitevisionScriptsBuild(projectRoot);
49
- if (!sitevisionResult.success) {
50
- setState({
51
- status: 'error',
52
- error: `${sitevisionResult.error}\n${sitevisionResult.output.slice(-1000)}`,
53
- warning,
54
- });
55
- return;
56
- }
57
- // sitevision-scripts already produced the zip; report it directly.
58
- const zipPath = getDelegatedZipPath(projectRoot, manifest.id);
59
- if (!zipExists(zipPath)) {
60
- setState({
61
- status: 'error',
62
- error: `Build reported success but no zip was found at ${zipPath}.`,
63
- warning,
64
- });
65
- return;
66
- }
67
- setState({
68
- status: 'success',
69
- message: 'Build complete',
70
- zipPath,
71
- zipSize: getZipSize(zipPath),
72
- warning,
73
- });
74
- return;
75
- }
76
- if (isBundled) {
77
- // Project ships its own webpack config: build it in-process.
78
- if (!WebpackRunner.isWebpackAvailable(projectRoot)) {
79
- setState({
80
- status: 'error',
81
- error: 'webpack not found. Run npm install to install dependencies.',
82
- });
83
- return;
84
- }
85
- setState({ status: 'building', message: 'Compiling with webpack...' });
86
- const appType = getAppType(manifest);
87
- const runner = new WebpackRunner(projectRoot, {
88
- mode: 'production',
89
- cssPrefix: manifest.id,
90
- restApp: appType !== 'web' && appType !== 'widget',
91
- });
92
- const result = await runner.run();
93
- await runner.close();
94
- if (!result.success) {
95
- setState({
96
- status: 'error',
97
- error: result.errors?.join('\n') || 'Build failed',
98
- result,
99
- });
100
- return;
101
- }
102
- // Copy static files after webpack build
103
- setState({ status: 'copying', message: 'Copying static files...' });
104
- copyStaticToBuild(projectRoot);
105
- setState(prev => ({ ...prev, result }));
106
- }
107
- else {
108
- // Non-bundled app: just copy files
109
- setState({ status: 'copying', message: 'Copying source files...' });
110
- copySrcToBuild(projectRoot);
111
- copyStaticToBuild(projectRoot);
112
- }
113
- // Step 3: Create zip if requested
114
- if (createZip) {
115
- setState({ status: 'zipping', message: 'Creating zip archive...' });
116
- const appId = getFullAppId(manifest.id);
117
- const zipPath = await createBuildZip(projectRoot, appId);
118
- const zipSize = getZipSize(zipPath);
119
- setState(prev => ({
120
- ...prev,
121
- status: 'success',
122
- message: 'Build complete',
123
- zipPath,
124
- zipSize,
125
- }));
126
- }
127
- else {
128
- setState(prev => ({
129
- ...prev,
130
- status: 'success',
131
- message: 'Build complete',
132
- }));
133
- }
134
- }
135
- catch (error) {
136
- setState({
137
- status: 'error',
138
- error: error instanceof Error ? error.message : String(error),
139
- });
140
- }
141
- }
142
- runBuild();
143
- }, [projectRoot, manifest, isBundled, createZip]);
144
- const getStatusIndicator = () => {
145
- switch (state.status) {
146
- case 'cleaning':
147
- case 'building':
148
- case 'copying':
149
- case 'zipping':
150
- return 'running';
151
- case 'success':
152
- return 'success';
153
- case 'error':
154
- return 'error';
155
- }
156
- };
157
- const getStatusLabel = () => {
158
- switch (state.status) {
159
- case 'cleaning':
160
- return 'Cleaning';
161
- case 'building':
162
- return 'Building';
163
- case 'copying':
164
- return 'Copying files';
165
- case 'zipping':
166
- return 'Creating zip';
167
- case 'success':
168
- return 'Build complete';
169
- case 'error':
170
- return 'Build failed';
171
- }
172
- };
173
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(StatusIndicator, { status: getStatusIndicator(), label: getStatusLabel(), message: state.message }) }), state.warning && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", state.warning] }) })), state.status === 'success' && state.result?.stats && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { dimColor: true, children: ["Compiled in ", state.result.stats.time, "ms"] }), state.result.stats.assets &&
174
- state.result.stats.assets.length > 0 && (_jsxs(Text, { dimColor: true, children: ["Assets: ", state.result.stats.assets.join(', ')] }))] })), state.status === 'success' && state.zipPath && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [_jsxs(Text, { color: "green", children: ["\u2713 Created: ", state.zipPath] }), state.zipSize !== undefined && (_jsxs(Text, { dimColor: true, children: [" Size: ", formatFileSize(state.zipSize)] }))] })), state.result?.warnings && state.result.warnings.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "Warnings:" }), state.result.warnings.slice(0, 5).map((warning, i) => (_jsx(Text, { color: "yellow", dimColor: true, children: warning.substring(0, 200) }, i))), state.result.warnings.length > 5 && (_jsxs(Text, { color: "yellow", dimColor: true, children: ["...and ", state.result.warnings.length - 5, " more"] }))] })), state.status === 'error' && state.error && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "red", children: state.error }) })), onBack && (state.status === 'success' || state.status === 'error') && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press q or Esc to return to menu" }) }))] }));
175
- }
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from 'ink';
3
+ import { TaskScreen } from './TaskScreen.js';
4
+ import { startBuild } from '../utils/tasks.js';
176
5
  export const buildCommand = {
177
6
  name: 'build',
178
7
  description: 'Build the application for production',
179
8
  requiresProject: true,
180
- flags: {
181
- 'no-zip': {
182
- type: 'boolean',
183
- description: 'Skip creating the zip archive',
184
- default: false,
185
- },
186
- },
187
9
  async execute({ project, flags }) {
188
- const createZip = !flags['no-zip'];
189
- const { waitUntilExit } = render(_jsx(BuildScreen, { projectRoot: project.root, manifest: project.manifest, createZip: createZip }));
190
- await waitUntilExit();
10
+ const task = startBuild(project, { zip: flags['zip'] !== false });
11
+ await render(_jsx(TaskScreen, { task: task })).waitUntilExit();
191
12
  },
192
13
  };