@hubspot/cli 7.4.9-experimental.0 → 7.5.1-experimental.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.
Files changed (38) hide show
  1. package/api/migrate.d.ts +2 -1
  2. package/api/migrate.js +7 -0
  3. package/bin/cli.js +6 -183
  4. package/commands/project/migrate.d.ts +8 -0
  5. package/commands/project/migrate.js +48 -0
  6. package/commands/project.js +2 -0
  7. package/lang/en.js +0 -2
  8. package/lang/en.lyaml +7 -1
  9. package/lib/app/migrate.d.ts +2 -2
  10. package/lib/app/migrate.js +71 -31
  11. package/lib/app/migrate_legacy.js +5 -3
  12. package/lib/middleware/__test__/configMiddleware.test.d.ts +1 -0
  13. package/lib/middleware/__test__/configMiddleware.test.js +194 -0
  14. package/lib/middleware/__test__/gitMiddleware.test.d.ts +1 -0
  15. package/lib/middleware/__test__/gitMiddleware.test.js +76 -0
  16. package/lib/middleware/__test__/notificationsMiddleware.test.d.ts +1 -0
  17. package/lib/middleware/__test__/notificationsMiddleware.test.js +10 -0
  18. package/lib/middleware/__test__/requestMiddleware.test.d.ts +1 -0
  19. package/lib/middleware/__test__/requestMiddleware.test.js +20 -0
  20. package/lib/middleware/__test__/utils.test.d.ts +1 -0
  21. package/lib/middleware/__test__/utils.test.js +53 -0
  22. package/lib/middleware/__test__/yargsChecksMiddleware.test.d.ts +1 -0
  23. package/lib/middleware/__test__/yargsChecksMiddleware.test.js +81 -0
  24. package/lib/middleware/configMiddleware.d.ts +13 -0
  25. package/lib/middleware/configMiddleware.js +105 -0
  26. package/lib/middleware/gitMiddleware.d.ts +2 -0
  27. package/lib/middleware/gitMiddleware.js +14 -0
  28. package/lib/middleware/notificationsMiddleware.d.ts +1 -0
  29. package/lib/middleware/notificationsMiddleware.js +38 -0
  30. package/lib/middleware/requestMiddleware.d.ts +1 -0
  31. package/lib/middleware/requestMiddleware.js +11 -0
  32. package/lib/middleware/utils.d.ts +8 -0
  33. package/lib/middleware/utils.js +17 -0
  34. package/lib/middleware/yargsChecksMiddleware.d.ts +4 -0
  35. package/lib/middleware/yargsChecksMiddleware.js +24 -0
  36. package/lib/projects/index.d.ts +3 -2
  37. package/package.json +2 -2
  38. package/types/Yargs.d.ts +6 -3
package/api/migrate.d.ts CHANGED
@@ -50,10 +50,11 @@ export interface MigrationSuccess extends MigrationBaseStatus {
50
50
  }
51
51
  export interface MigrationFailed extends MigrationBaseStatus {
52
52
  status: typeof MIGRATION_STATUS.FAILURE;
53
- projectErrorsDetail?: string;
53
+ projectErrorDetail: string;
54
54
  componentErrorDetails: Record<string, string>;
55
55
  }
56
56
  export type MigrationStatus = MigrationInProgress | MigrationInputRequired | MigrationSuccess | MigrationFailed;
57
+ export declare function isMigrationStatus(error: unknown): error is MigrationStatus;
57
58
  export declare function listAppsForMigration(accountId: number): HubSpotPromise<ListAppsResponse>;
58
59
  export declare function initializeMigration(accountId: number, applicationId: number, platformVersion: string): HubSpotPromise<InitializeMigrationResponse>;
59
60
  export declare function continueMigration(portalId: number, migrationId: number, componentUids: Record<string, string>, projectName: string): HubSpotPromise<ContinueMigrationResponse>;
package/api/migrate.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isMigrationStatus = isMigrationStatus;
3
4
  exports.listAppsForMigration = listAppsForMigration;
4
5
  exports.initializeMigration = initializeMigration;
5
6
  exports.continueMigration = continueMigration;
@@ -7,6 +8,12 @@ exports.checkMigrationStatusV2 = checkMigrationStatusV2;
7
8
  const projects_1 = require("@hubspot/local-dev-lib/constants/projects");
8
9
  const http_1 = require("@hubspot/local-dev-lib/http");
9
10
  const MIGRATIONS_API_PATH_V2 = 'dfs/migrations/v2';
11
+ function isMigrationStatus(error) {
12
+ return (typeof error === 'object' &&
13
+ error !== null &&
14
+ 'id' in error &&
15
+ 'status' in error);
16
+ }
10
17
  async function listAppsForMigration(accountId) {
11
18
  return http_1.http.get(accountId, {
12
19
  url: `${MIGRATIONS_API_PATH_V2}/list-apps`,
package/bin/cli.js CHANGED
@@ -1,21 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  const yargs = require('yargs');
4
- const updateNotifier = require('update-notifier');
5
- const chalk = require('chalk');
6
4
  const { logger } = require('@hubspot/local-dev-lib/logger');
7
- const { addUserAgentHeader } = require('@hubspot/local-dev-lib/http');
8
- const { loadConfig, getAccountId, configFileExists, getConfigPath, validateConfig, } = require('@hubspot/local-dev-lib/config');
9
5
  const { logError } = require('../lib/errorHandlers/index');
10
6
  const { setLogLevel, getCommandName } = require('../lib/commonOpts');
11
- const { validateAccount } = require('../lib/validation');
12
7
  const { trackHelpUsage, trackConvertFieldsUsage, } = require('../lib/usageTracking');
13
- const { getIsInProject } = require('../lib/projects');
14
- const pkg = require('../package.json');
15
- const { i18n } = require('../lib/lang');
16
8
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
17
- const { UI_COLORS, uiCommandReference, uiDeprecatedTag } = require('../lib/ui');
18
- const { checkAndWarnGitInclusion } = require('../lib/ui/git');
9
+ const { loadConfigMiddleware, injectAccountIdMiddleware, validateAccountOptions, handleDeprecatedEnvVariables, } = require('../lib/middleware/configMiddleware');
10
+ const { notifyAboutUpdates, } = require('../lib/middleware/notificationsMiddleware');
11
+ const { checkAndWarnGitInclusionMiddleware, } = require('../lib/middleware/gitMiddleware');
12
+ const { performChecks } = require('../lib/middleware/yargsChecksMiddleware');
13
+ const { setRequestHeaders } = require('../lib/middleware/requestMiddleware');
19
14
  const removeCommand = require('../commands/remove');
20
15
  const initCommand = require('../commands/init');
21
16
  const logsCommand = require('../commands/logs');
@@ -44,33 +39,7 @@ const feedbackCommand = require('../commands/feedback');
44
39
  const doctorCommand = require('../commands/doctor');
45
40
  const completionCommand = require('../commands/completion');
46
41
  const appCommand = require('../commands/app');
47
- const notifier = updateNotifier({
48
- pkg: { ...pkg, name: '@hubspot/cli' },
49
- distTag: 'latest',
50
- shouldNotifyInNpmScript: true,
51
- });
52
- const CMS_CLI_PACKAGE_NAME = '@hubspot/cms-cli';
53
- notifier.notify({
54
- message: pkg.name === CMS_CLI_PACKAGE_NAME
55
- ? i18n(`commands.generalErrors.updateNotify.cmsUpdateNotification`, {
56
- packageName: CMS_CLI_PACKAGE_NAME,
57
- updateCommand: uiCommandReference('{updateCommand}'),
58
- })
59
- : i18n(`commands.generalErrors.updateNotify.cliUpdateNotification`, {
60
- updateCommand: uiCommandReference('{updateCommand}'),
61
- }),
62
- defer: false,
63
- boxenOptions: {
64
- borderColor: UI_COLORS.MARIGOLD_DARK,
65
- margin: 1,
66
- padding: 1,
67
- textAlignment: 'center',
68
- borderStyle: 'round',
69
- title: pkg.name === CMS_CLI_PACKAGE_NAME
70
- ? null
71
- : chalk.bold(i18n(`commands.generalErrors.updateNotify.notifyTitle`)),
72
- },
73
- });
42
+ notifyAboutUpdates();
74
43
  const getTerminalWidth = () => {
75
44
  const width = yargs.terminalWidth();
76
45
  if (width >= 100)
@@ -92,152 +61,6 @@ const handleFailure = (msg, err, yargs) => {
92
61
  process.exit(EXIT_CODES.ERROR);
93
62
  }
94
63
  };
95
- const performChecks = argv => {
96
- // "hs config set default-account" has moved to "hs accounts use"
97
- if (argv._[0] === 'config' &&
98
- argv._[1] === 'set' &&
99
- argv._[2] === 'default-account') {
100
- logger.error(i18n(`commands.generalErrors.setDefaultAccountMoved`));
101
- process.exit(EXIT_CODES.ERROR);
102
- }
103
- // Require "project" command when running upload/watch inside of a project
104
- if (argv._.length === 1 && ['upload', 'watch'].includes(argv._[0])) {
105
- if (getIsInProject(argv.src)) {
106
- logger.error(i18n(`commands.generalErrors.srcIsProject`, {
107
- src: argv.src || './',
108
- command: argv._.join(' '),
109
- }));
110
- process.exit(EXIT_CODES.ERROR);
111
- }
112
- else {
113
- return true;
114
- }
115
- }
116
- else {
117
- return true;
118
- }
119
- };
120
- const setRequestHeaders = () => {
121
- addUserAgentHeader('HubSpot CLI', pkg.version);
122
- };
123
- const isTargetedCommand = (options, commandMap) => {
124
- const checkCommand = (options, commandMap) => {
125
- const currentCommand = options._[0];
126
- if (!commandMap[currentCommand]) {
127
- return false;
128
- }
129
- if (commandMap[currentCommand].target) {
130
- return true;
131
- }
132
- const subCommands = commandMap[currentCommand].subCommands || {};
133
- if (options._.length > 1) {
134
- return checkCommand({ _: options._.slice(1) }, subCommands);
135
- }
136
- return false;
137
- };
138
- return checkCommand(options, commandMap);
139
- };
140
- const SKIP_CONFIG_VALIDATION = {
141
- init: { target: true },
142
- auth: { target: true },
143
- };
144
- const handleDeprecatedEnvVariables = options => {
145
- // HUBSPOT_PORTAL_ID is deprecated, but we'll still support it for now
146
- // The HubSpot GH Deploy Action still uses HUBSPOT_PORTAL_ID
147
- if (options.useEnv &&
148
- process.env.HUBSPOT_PORTAL_ID &&
149
- !process.env.HUBSPOT_ACCOUNT_ID) {
150
- uiDeprecatedTag(i18n(`commands.generalErrors.handleDeprecatedEnvVariables.portalEnvVarDeprecated`, {
151
- configPath: getConfigPath(),
152
- }));
153
- process.env.HUBSPOT_ACCOUNT_ID = process.env.HUBSPOT_PORTAL_ID;
154
- }
155
- };
156
- /**
157
- * Auto-injects the derivedAccountId flag into all commands
158
- */
159
- const injectAccountIdMiddleware = async (options) => {
160
- const { account } = options;
161
- // Preserves the original --account flag for certain commands.
162
- options.providedAccountId = account;
163
- if (options.useEnv && process.env.HUBSPOT_ACCOUNT_ID) {
164
- options.derivedAccountId = parseInt(process.env.HUBSPOT_ACCOUNT_ID, 10);
165
- }
166
- else {
167
- options.derivedAccountId = getAccountId(account);
168
- }
169
- };
170
- const loadConfigMiddleware = async (options) => {
171
- // Skip this when no command is provided
172
- if (!options._.length) {
173
- return;
174
- }
175
- const maybeValidateConfig = () => {
176
- if (!isTargetedCommand(options, SKIP_CONFIG_VALIDATION) &&
177
- !validateConfig()) {
178
- process.exit(EXIT_CODES.ERROR);
179
- }
180
- };
181
- if (configFileExists(true) && options.config) {
182
- logger.error(i18n(`commands.generalErrors.loadConfigMiddleware.configFileExists`, {
183
- configPath: getConfigPath(),
184
- }));
185
- process.exit(EXIT_CODES.ERROR);
186
- }
187
- else if (!isTargetedCommand(options, { init: { target: true } })) {
188
- const { config: configPath } = options;
189
- const config = loadConfig(configPath, options);
190
- // We don't run validateConfig() for auth because users should be able to run it when
191
- // no accounts are configured, but we still want to exit if the config file is not found
192
- if (isTargetedCommand(options, { auth: { target: true } }) && !config) {
193
- process.exit(EXIT_CODES.ERROR);
194
- }
195
- }
196
- maybeValidateConfig();
197
- };
198
- const checkAndWarnGitInclusionMiddleware = options => {
199
- // Skip this when no command is provided
200
- if (!options._.length) {
201
- return;
202
- }
203
- checkAndWarnGitInclusion(getConfigPath());
204
- };
205
- const accountsSubCommands = {
206
- target: false,
207
- subCommands: {
208
- clean: { target: true },
209
- list: { target: true },
210
- ls: { target: true },
211
- remove: { target: true },
212
- },
213
- };
214
- const sandboxesSubCommands = {
215
- target: false,
216
- subCommands: {
217
- delete: { target: true },
218
- },
219
- };
220
- const SKIP_ACCOUNT_VALIDATION = {
221
- init: { target: true },
222
- auth: { target: true },
223
- account: accountsSubCommands,
224
- accounts: accountsSubCommands,
225
- sandbox: sandboxesSubCommands,
226
- sandboxes: sandboxesSubCommands,
227
- };
228
- const validateAccountOptions = async (options) => {
229
- // Skip this when no command is provided
230
- if (!options._.length) {
231
- return;
232
- }
233
- let validAccount = true;
234
- if (!isTargetedCommand(options, SKIP_ACCOUNT_VALIDATION)) {
235
- validAccount = await validateAccount(options);
236
- }
237
- if (!validAccount) {
238
- process.exit(EXIT_CODES.ERROR);
239
- }
240
- };
241
64
  const argv = yargs
242
65
  .usage('The command line interface to interact with HubSpot.')
243
66
  // loadConfigMiddleware loads the new hidden config for all commands
@@ -0,0 +1,8 @@
1
+ import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs';
2
+ import { ProjectMigrateOptions } from '../../types/Yargs';
3
+ export declare const command = "migrate";
4
+ export declare const describe: undefined;
5
+ export declare function handler(options: ArgumentsCamelCase<ProjectMigrateOptions>): Promise<undefined>;
6
+ export declare function builder(yargs: Argv): Argv<ProjectMigrateOptions>;
7
+ declare const migrateAppCommand: CommandModule<unknown, ProjectMigrateOptions>;
8
+ export default migrateAppCommand;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describe = exports.command = void 0;
4
+ exports.handler = handler;
5
+ exports.builder = builder;
6
+ const lang_1 = require("../../lib/lang");
7
+ const logger_1 = require("@hubspot/local-dev-lib/logger");
8
+ const commonOpts_1 = require("../../lib/commonOpts");
9
+ const migrate_1 = require("../../lib/app/migrate");
10
+ const projects_1 = require("../../lib/projects");
11
+ const projects_2 = require("@hubspot/local-dev-lib/constants/projects");
12
+ const errorHandlers_1 = require("../../lib/errorHandlers");
13
+ const exitCodes_1 = require("../../lib/enums/exitCodes");
14
+ exports.command = 'migrate';
15
+ exports.describe = undefined; // i18n('commands.project.subcommands.migrate.noProjectConfig')
16
+ async function handler(options) {
17
+ const projectConfig = await (0, projects_1.getProjectConfig)();
18
+ if (!projectConfig) {
19
+ logger_1.logger.error((0, lang_1.i18n)('commands.project.subcommands.migrate.errors.noProjectConfig'));
20
+ return;
21
+ }
22
+ const { derivedAccountId } = options;
23
+ try {
24
+ await (0, migrate_1.migrateApp2025_2)(derivedAccountId, {
25
+ ...options,
26
+ name: projectConfig?.projectConfig?.name,
27
+ platformVersion: projects_2.PLATFORM_VERSIONS.unstable,
28
+ }, projectConfig);
29
+ }
30
+ catch (error) {
31
+ (0, errorHandlers_1.logError)(error);
32
+ return process.exit(exitCodes_1.EXIT_CODES.ERROR);
33
+ }
34
+ return process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
35
+ }
36
+ function builder(yargs) {
37
+ (0, commonOpts_1.addConfigOptions)(yargs);
38
+ (0, commonOpts_1.addAccountOptions)(yargs);
39
+ (0, commonOpts_1.addGlobalOptions)(yargs);
40
+ return yargs;
41
+ }
42
+ const migrateAppCommand = {
43
+ command: exports.command,
44
+ describe: exports.describe,
45
+ handler,
46
+ builder,
47
+ };
48
+ exports.default = migrateAppCommand;
@@ -15,6 +15,7 @@ const open = require('./project/open');
15
15
  const dev = require('./project/dev');
16
16
  const add = require('./project/add');
17
17
  const migrateApp = require('./project/migrateApp');
18
+ const migrate = require('./project/migrate');
18
19
  const cloneApp = require('./project/cloneApp');
19
20
  const installDeps = require('./project/installDeps');
20
21
  const i18nKey = 'commands.project';
@@ -34,6 +35,7 @@ exports.builder = yargs => {
34
35
  .command(download)
35
36
  .command(open)
36
37
  .command(migrateApp)
38
+ .command(migrate)
37
39
  .command(cloneApp)
38
40
  .command(installDeps)
39
41
  .demandCommand(1, '');
package/lang/en.js CHANGED
@@ -11,8 +11,6 @@ export const commands = {
11
11
  },
12
12
  srcIsProject: (src, command) =>
13
13
  `"${src}" is in a project folder. Did you mean "hs project ${command}"?`,
14
- setDefaultAccountMoved:
15
- 'This command has moved. Try `hs accounts use` instead',
16
14
  handleDeprecatedEnvVariables: {
17
15
  portalEnvVarDeprecated:
18
16
  'The HUBSPOT_PORTAL_ID environment variable is deprecated. Please use HUBSPOT_ACCOUNT_ID instead.',
package/lang/en.lyaml CHANGED
@@ -6,7 +6,6 @@ en:
6
6
  cmsUpdateNotification: "{{#bold}}The CMS CLI is now the HubSpot CLI{{/bold}}\n\nTo upgrade, uninstall {{#bold}}{{ packageName }}{{/bold}}\nand then run {{ updateCommand }}"
7
7
  cliUpdateNotification: "HubSpot CLI version {{#cyan}}{{#bold}}{currentVersion}{{/bold}}{{/cyan}} is outdated.\nRun {{ updateCommand }} to upgrade to version {{#cyan}}{{#bold}}{latestVersion}{{/bold}}{{/cyan}}"
8
8
  srcIsProject: "\"{{ src }}\" is in a project folder. Did you mean \"hs project {{command}}\"?"
9
- setDefaultAccountMoved: "This command has moved. Try `hs accounts use` instead"
10
9
  handleDeprecatedEnvVariables:
11
10
  portalEnvVarDeprecated: "The HUBSPOT_PORTAL_ID environment variable is deprecated. Please use HUBSPOT_ACCOUNT_ID instead."
12
11
  loadConfigMiddleware:
@@ -589,6 +588,7 @@ en:
589
588
  componentsToBeMigrated: "The following component types will be migrated: {{ components }}"
590
589
  componentsThatWillNotBeMigrated: "[NOTE] These component types are not yet supported for migration but will be available later: {{ components }}"
591
590
  errors:
591
+ noAppsForProject: "No apps associated with project {{ projectName }}"
592
592
  noAppsEligible: "No apps in account {{ accountId }} are currently migratable"
593
593
  noAccountConfig: "There is no account associated with {{ accountId }} in the config file. Please choose another account and try again, or authenticate {{ accountId }} using {{ authCommand }}."
594
594
  invalidAccountTypeTitle: "{{#bold}}Developer account not targeted{{/bold}}"
@@ -656,6 +656,12 @@ en:
656
656
  examples:
657
657
  default: "Create a component within your project"
658
658
  withFlags: "Use --name and --type flags to bypass the prompt."
659
+ migrate:
660
+ describe: "Migrate a project to the projects framework."
661
+ errors:
662
+ noProjectConfig: "No project detected. Please run this command again from a project directory."
663
+ examples:
664
+ default: "Migrate a project to the projects framework"
659
665
  deploy:
660
666
  describe: "Deploy a project build."
661
667
  deployBuildIdPrompt: "[--build] Deploy which build?"
@@ -1,5 +1,5 @@
1
1
  import { ArgumentsCamelCase } from 'yargs';
2
+ import { LoadedProjectConfig } from '../projects';
2
3
  import { MigrateAppOptions } from '../../types/Yargs';
3
- export declare function downloadProjectFiles(derivedAccountId: number, projectName: string, buildId: number, projectDest: string): Promise<void>;
4
- export declare function migrateApp2025_2(derivedAccountId: number, options: ArgumentsCamelCase<MigrateAppOptions>): Promise<void>;
4
+ export declare function migrateApp2025_2(derivedAccountId: number, options: ArgumentsCamelCase<MigrateAppOptions>, projectConfig?: LoadedProjectConfig): Promise<void>;
5
5
  export declare function logInvalidAccountError(i18nKey: string): void;
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.downloadProjectFiles = downloadProjectFiles;
7
6
  exports.migrateApp2025_2 = migrateApp2025_2;
8
7
  exports.logInvalidAccountError = logInvalidAccountError;
9
8
  const logger_1 = require("@hubspot/local-dev-lib/logger");
@@ -23,6 +22,7 @@ const projects_3 = require("../projects");
23
22
  const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
24
23
  const polling_1 = require("../polling");
25
24
  const migrate_1 = require("../../api/migrate");
25
+ const fs_1 = __importDefault(require("fs"));
26
26
  function getUnmigratableReason(reasonCode) {
27
27
  switch (reasonCode) {
28
28
  case projects_1.UNMIGRATABLE_REASONS.UP_TO_DATE:
@@ -37,16 +37,32 @@ function getUnmigratableReason(reasonCode) {
37
37
  });
38
38
  }
39
39
  }
40
- async function handleMigrationSetup(derivedAccountId, options) {
40
+ function filterAppsByProjectName(projectConfig) {
41
+ return (app) => {
42
+ if (projectConfig) {
43
+ return app.projectName === projectConfig?.projectConfig?.name;
44
+ }
45
+ return !app.projectName;
46
+ };
47
+ }
48
+ async function handleMigrationSetup(derivedAccountId, options, projectConfig) {
41
49
  const { name, dest, appId } = options;
42
- const { data } = await (0, migrate_1.listAppsForMigration)(derivedAccountId);
43
- const { migratableApps, unmigratableApps } = data;
44
- const allApps = [...migratableApps, ...unmigratableApps].filter(app => !app.projectName);
50
+ const { data: { migratableApps, unmigratableApps }, } = await (0, migrate_1.listAppsForMigration)(derivedAccountId);
51
+ const filteredMigratableApps = migratableApps.filter(filterAppsByProjectName(projectConfig));
52
+ const filteredUnmigratableApps = unmigratableApps.filter(filterAppsByProjectName(projectConfig));
53
+ const allApps = [...filteredMigratableApps, ...filteredUnmigratableApps];
54
+ if (allApps.length > 1 && projectConfig) {
55
+ throw new Error('Multiple apps found in project, this is not allowed in the new system');
56
+ }
45
57
  if (allApps.length === 0) {
46
- const reasons = unmigratableApps.map(app => `${chalk_1.default.bold(app.appName)}: ${getUnmigratableReason(app.unmigratableReason)}`);
58
+ const reasons = filteredUnmigratableApps.map(app => `${chalk_1.default.bold(app.appName)}: ${getUnmigratableReason(app.unmigratableReason)}`);
59
+ if (projectConfig) {
60
+ // TODO: i18n, get copy from UX
61
+ throw new Error('No migratable apps in project');
62
+ }
47
63
  throw new Error(`${(0, lang_1.i18n)(`commands.project.subcommands.migrateApp.errors.noAppsEligible`, {
48
- accountId: derivedAccountId,
49
- })} \n - ${reasons.join('\n - ')}`);
64
+ accountId: (0, ui_1.uiAccountDescription)(derivedAccountId),
65
+ })}${reasons.length ? `\n - ${reasons.join('\n - ')}` : ''}`);
50
66
  }
51
67
  if (appId &&
52
68
  !allApps.some(app => {
@@ -98,6 +114,16 @@ async function handleMigrationSetup(derivedAccountId, options) {
98
114
  if (!proceed) {
99
115
  return {};
100
116
  }
117
+ // If it's a project we don't want to prompt for dest and name, so just return early
118
+ if (projectConfig &&
119
+ projectConfig?.projectConfig &&
120
+ projectConfig?.projectDir) {
121
+ return {
122
+ appIdToMigrate,
123
+ projectName: projectConfig.projectConfig.name,
124
+ projectDest: projectConfig.projectDir,
125
+ };
126
+ }
101
127
  const projectName = name ||
102
128
  (await (0, promptUtils_1.inputPrompt)((0, lang_1.i18n)('commands.project.subcommands.migrateApp.prompt.inputName')));
103
129
  const { projectExists } = await (0, projects_3.ensureProjectExists)(derivedAccountId, projectName, { allowCreate: false, noLogs: true });
@@ -133,8 +159,8 @@ async function beginMigration(derivedAccountId, appId, platformVersion) {
133
159
  const { componentHint, componentType } = component;
134
160
  uidMap[componentId] = await (0, promptUtils_1.inputPrompt)((0, lang_1.i18n)('commands.project.subcommands.migrateApp.prompt.uidForComponent', {
135
161
  componentName: componentHint
136
- ? `${componentHint} [${componentType}]`
137
- : componentType,
162
+ ? `${(0, transform_1.mapToUserFacingType)(componentType)} '${componentHint}'`
163
+ : (0, transform_1.mapToUserFacingType)(componentType),
138
164
  }), {
139
165
  validate: (uid) => {
140
166
  const result = (0, project_parsing_lib_1.validateUid)(uid);
@@ -169,37 +195,46 @@ async function finalizeMigration(derivedAccountId, migrationId, uidMap, projectN
169
195
  SpinniesManager_1.default.fail('finishingMigration', {
170
196
  text: (0, lang_1.i18n)(`commands.project.subcommands.migrateApp.spinners.migrationFailed`),
171
197
  });
172
- throw error;
198
+ if ((0, migrate_1.isMigrationStatus)(error) && error.status === Migration_1.MIGRATION_STATUS.FAILURE) {
199
+ throw new Error(error.projectErrorDetail);
200
+ }
201
+ throw new Error((0, lang_1.i18n)('commands.project.subcommands.migrateApp.errors.migrationFailed'), {
202
+ cause: error,
203
+ });
204
+ }
205
+ if (pollResponse.status !== Migration_1.MIGRATION_STATUS.SUCCESS) {
206
+ throw new Error((0, lang_1.i18n)('commands.project.subcommands.migrateApp.errors.migrationFailed'));
173
207
  }
174
208
  if (pollResponse.status === Migration_1.MIGRATION_STATUS.SUCCESS) {
175
209
  SpinniesManager_1.default.succeed('finishingMigration', {
176
210
  text: (0, lang_1.i18n)(`commands.project.subcommands.migrateApp.spinners.migrationComplete`),
177
211
  });
178
- return pollResponse.buildId;
179
- }
180
- else {
181
- SpinniesManager_1.default.fail('finishingMigration', {
182
- text: (0, lang_1.i18n)(`commands.project.subcommands.migrateApp.spinners.migrationFailed`),
183
- });
184
- if (pollResponse.status === Migration_1.MIGRATION_STATUS.FAILURE) {
185
- logger_1.logger.error(pollResponse.componentErrorDetails);
186
- throw new Error(pollResponse.projectErrorsDetail);
187
- }
188
- throw new Error((0, lang_1.i18n)('commands.project.subcommands.migrateApp.errors.migrationFailed'));
189
212
  }
213
+ return pollResponse.buildId;
190
214
  }
191
- async function downloadProjectFiles(derivedAccountId, projectName, buildId, projectDest) {
215
+ async function downloadProjectFiles(derivedAccountId, projectName, buildId, projectDest, projectConfig) {
192
216
  try {
193
217
  SpinniesManager_1.default.add('fetchingMigratedProject', {
194
218
  text: (0, lang_1.i18n)(`commands.project.subcommands.migrateApp.spinners.downloadingProjectContents`),
195
219
  });
196
220
  const { data: zippedProject } = await (0, projects_2.downloadProject)(derivedAccountId, projectName, buildId);
197
- const absoluteDestPath = projectDest
198
- ? path_1.default.resolve((0, path_2.getCwd)(), projectDest)
199
- : (0, path_2.getCwd)();
221
+ let absoluteDestPath;
222
+ if (projectConfig?.projectConfig && projectConfig?.projectDir) {
223
+ const { projectDir } = projectConfig;
224
+ absoluteDestPath = projectDir;
225
+ const { srcDir } = projectConfig.projectConfig;
226
+ const archiveDest = path_1.default.join(projectDir, 'archive');
227
+ // Move the existing source directory to archive
228
+ fs_1.default.renameSync(path_1.default.join(projectDir, srcDir), archiveDest);
229
+ }
230
+ else {
231
+ absoluteDestPath = projectDest
232
+ ? path_1.default.resolve((0, path_2.getCwd)(), projectDest)
233
+ : (0, path_2.getCwd)();
234
+ }
200
235
  await (0, archive_1.extractZipArchive)(zippedProject, (0, path_2.sanitizeFileName)(projectName), absoluteDestPath, {
201
236
  includesRootDir: true,
202
- hideLogs: false,
237
+ hideLogs: true,
203
238
  });
204
239
  SpinniesManager_1.default.succeed('fetchingMigratedProject', {
205
240
  text: (0, lang_1.i18n)(`commands.project.subcommands.migrateApp.spinners.downloadingProjectContentsComplete`),
@@ -213,9 +248,14 @@ async function downloadProjectFiles(derivedAccountId, projectName, buildId, proj
213
248
  throw error;
214
249
  }
215
250
  }
216
- async function migrateApp2025_2(derivedAccountId, options) {
251
+ async function migrateApp2025_2(derivedAccountId, options, projectConfig) {
217
252
  SpinniesManager_1.default.init();
218
- const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options);
253
+ if (projectConfig &&
254
+ (!projectConfig?.projectConfig || !projectConfig?.projectDir)) {
255
+ // TODO: i18n
256
+ throw new Error('Invalid project config');
257
+ }
258
+ const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
219
259
  if (!appIdToMigrate || !projectName || !projectDest) {
220
260
  return;
221
261
  }
@@ -224,8 +264,8 @@ async function migrateApp2025_2(derivedAccountId, options) {
224
264
  return;
225
265
  }
226
266
  const { migrationId, uidMap } = migrationInProgress;
227
- const buildId = await finalizeMigration(derivedAccountId, migrationId, uidMap, projectName);
228
- await downloadProjectFiles(derivedAccountId, projectName, buildId, projectDest);
267
+ const buildId = await finalizeMigration(derivedAccountId, migrationId, uidMap, projectConfig?.projectConfig?.name || projectName);
268
+ await downloadProjectFiles(derivedAccountId, projectName, buildId, projectDest, projectConfig);
229
269
  }
230
270
  function logInvalidAccountError(i18nKey) {
231
271
  (0, ui_1.uiLine)();
@@ -31,13 +31,15 @@ async function migrateApp2023_2(derivedAccountId, options, accountConfig) {
31
31
  (0, migrate_1.logInvalidAccountError)('commands.project.subcommands.migrateApp');
32
32
  process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
33
33
  }
34
- const { appId } = 'appId' in options
35
- ? options
36
- : await (0, selectPublicAppPrompt_1.selectPublicAppPrompt)({
34
+ let appId = options.appId;
35
+ if (!appId) {
36
+ const { appId: selectAppId } = await (0, selectPublicAppPrompt_1.selectPublicAppPrompt)({
37
37
  accountId: derivedAccountId,
38
38
  accountName,
39
39
  isMigratingApp: true,
40
40
  });
41
+ appId = selectAppId;
42
+ }
41
43
  try {
42
44
  const { data: selectedApp } = await (0, appsDev_1.fetchPublicAppMetadata)(appId, derivedAccountId);
43
45
  // preventProjectMigrations returns true if we have not added app to allowlist config.
@@ -0,0 +1 @@
1
+ export {};