@hubspot/cli 8.0.9-experimental.0 → 8.0.10-experimental.1

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.
@@ -1,16 +1,14 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import cliProgress from 'cli-progress';
4
3
  import { commands } from '../../../lang/en.js';
5
4
  import { getCwd } from '@hubspot/local-dev-lib/path';
6
- import { FILE_UPLOAD_RESULT_TYPES } from '@hubspot/local-dev-lib/constants/files';
7
5
  import { getThemeJSONPath } from '@hubspot/local-dev-lib/cms/themes';
8
- import { createDevServer } from '@hubspot/cms-dev-server';
9
- import { getUploadableFileList } from '../../../lib/upload.js';
6
+ // Use subprocess approach to avoid React version conflicts
7
+ // import { createDevServer } from '@hubspot/cms-dev-server';
8
+ import { spawnDevServer } from '../../../lib/cms/devServerProcess.js';
10
9
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
11
10
  import { previewPrompt, previewProjectPrompt, } from '../../../lib/prompts/previewPrompt.js';
12
11
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
13
- import { ApiErrorContext, logError } from '../../../lib/errorHandlers/index.js';
14
12
  import { getProjectConfig } from '../../../lib/projects/config.js';
15
13
  import { findProjectComponents } from '../../../lib/projects/structure.js';
16
14
  import { ComponentTypes } from '../../../types/Projects.js';
@@ -73,67 +71,16 @@ async function determineSrcAndDest(args) {
73
71
  async function handler(args) {
74
72
  const { derivedAccountId, noSsl, resetSession, port, generateFieldsTypes } = args;
75
73
  const { absoluteSrc, dest } = await determineSrcAndDest(args);
76
- const filePaths = await getUploadableFileList(absoluteSrc, false);
77
- function startProgressBar(numFiles) {
78
- const initialUploadProgressBar = new cliProgress.SingleBar({
79
- gracefulExit: true,
80
- format: '[{bar}] {percentage}% | {value}/{total} | {label}',
81
- hideCursor: true,
82
- }, cliProgress.Presets.rect);
83
- initialUploadProgressBar.start(numFiles, 0, {
84
- label: commands.cms.subcommands.theme.subcommands.preview
85
- .initialUploadProgressBar.start,
86
- });
87
- let uploadsHaveStarted = false;
88
- const uploadOptions = {
89
- onAttemptCallback: () => {
90
- /* Intentionally blank */
91
- },
92
- onSuccessCallback: () => {
93
- initialUploadProgressBar.increment();
94
- if (!uploadsHaveStarted) {
95
- uploadsHaveStarted = true;
96
- initialUploadProgressBar.update(0, {
97
- label: commands.cms.subcommands.theme.subcommands.preview
98
- .initialUploadProgressBar.uploading,
99
- });
100
- }
101
- },
102
- onFirstErrorCallback: () => {
103
- /* Intentionally blank */
104
- },
105
- onRetryCallback: () => {
106
- /* Intentionally blank */
107
- },
108
- onFinalErrorCallback: () => initialUploadProgressBar.increment(),
109
- onFinishCallback: (results) => {
110
- initialUploadProgressBar.update(numFiles, {
111
- label: commands.cms.subcommands.theme.subcommands.preview
112
- .initialUploadProgressBar.finish,
113
- });
114
- initialUploadProgressBar.stop();
115
- results.forEach(result => {
116
- if (result.resultType == FILE_UPLOAD_RESULT_TYPES.FAILURE) {
117
- uiLogger.error(commands.cms.subcommands.theme.subcommands.preview.errors.uploadFailed(result.file, dest));
118
- logError(result.error, new ApiErrorContext({
119
- accountId: derivedAccountId,
120
- request: dest,
121
- payload: result.file,
122
- }));
123
- }
124
- });
125
- },
126
- };
127
- return uploadOptions;
128
- }
129
74
  trackCommandUsage('preview', {}, derivedAccountId);
130
- if (port) {
131
- process.env['PORT'] = port.toString();
132
- }
133
- createDevServer(absoluteSrc, false, '', '', !noSsl, generateFieldsTypes, {
134
- filePaths,
75
+ // Spawn dev server in isolated subprocess to avoid React version conflicts
76
+ // File listing and progress bars are handled within the subprocess
77
+ spawnDevServer({
78
+ absoluteSrc,
79
+ accountName: derivedAccountId?.toString(),
80
+ noSsl,
81
+ port,
82
+ generateFieldsTypes,
135
83
  resetSession: resetSession || false,
136
- startProgressBar,
137
84
  dest,
138
85
  });
139
86
  }
@@ -0,0 +1,13 @@
1
+ import { ChildProcess } from 'child_process';
2
+ interface DevServerOptions {
3
+ absoluteSrc: string;
4
+ accountName?: string;
5
+ configPath?: string;
6
+ noSsl?: boolean;
7
+ port?: number;
8
+ generateFieldsTypes?: boolean;
9
+ resetSession?: boolean;
10
+ dest?: string;
11
+ }
12
+ export declare function spawnDevServer(options: DevServerOptions): ChildProcess;
13
+ export {};
@@ -0,0 +1,200 @@
1
+ import { spawn, execSync } from 'child_process';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import os from 'os';
5
+ import { fileURLToPath } from 'url';
6
+ import { getConfigFilePath } from '@hubspot/local-dev-lib/config';
7
+ /**
8
+ * Ensures cms-dev-server is installed in an isolated cache directory.
9
+ * This prevents React version conflicts with the CLI.
10
+ */
11
+ function ensureCmsDevServerCache(targetVersion) {
12
+ const cacheDir = path.join(os.homedir(), '.hubspot', 'cms-dev-server-cache');
13
+ const packageJsonPath = path.join(cacheDir, 'node_modules', '@hubspot', 'cms-dev-server', 'package.json');
14
+ // Check if already installed with correct version
15
+ let needsInstall = true;
16
+ if (fs.existsSync(packageJsonPath)) {
17
+ try {
18
+ const installedPackage = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
19
+ if (installedPackage.version === targetVersion) {
20
+ needsInstall = false;
21
+ }
22
+ }
23
+ catch (e) {
24
+ // If we can't read the package.json, reinstall
25
+ needsInstall = true;
26
+ }
27
+ }
28
+ if (needsInstall) {
29
+ console.log(`Setting up isolated cms-dev-server ${targetVersion}...`);
30
+ // Create cache directory
31
+ fs.mkdirSync(cacheDir, { recursive: true });
32
+ // Clear old installation if exists
33
+ const nodeModulesDir = path.join(cacheDir, 'node_modules');
34
+ if (fs.existsSync(nodeModulesDir)) {
35
+ fs.rmSync(nodeModulesDir, { recursive: true, force: true });
36
+ }
37
+ // Install cms-dev-server with production dependencies only
38
+ try {
39
+ execSync(`npm install @hubspot/cms-dev-server@${targetVersion} --production --no-save --loglevel=error`, {
40
+ cwd: cacheDir,
41
+ stdio: 'ignore', // Suppress npm output
42
+ });
43
+ console.log('✓ cms-dev-server setup complete');
44
+ }
45
+ catch (e) {
46
+ console.error('Failed to install cms-dev-server:', e);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ return cacheDir;
51
+ }
52
+ export function spawnDevServer(options) {
53
+ const { absoluteSrc, accountName, noSsl, port, generateFieldsTypes, resetSession, dest, } = options;
54
+ // Get the version from CLI's package.json
55
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
56
+ const cliPackageJsonPath = path.join(__dirname, '../../package.json');
57
+ const cliPackageJson = JSON.parse(fs.readFileSync(cliPackageJsonPath, 'utf8'));
58
+ const targetVersion = cliPackageJson.dependencies['@hubspot/cms-dev-server'];
59
+ if (!targetVersion) {
60
+ console.error('Could not determine cms-dev-server version from package.json');
61
+ process.exit(1);
62
+ }
63
+ // Ensure cms-dev-server is installed in isolated cache
64
+ const cacheDir = ensureCmsDevServerCache(targetVersion);
65
+ // Build a Node script that imports and calls createDevServer
66
+ // This runs in the isolated cache directory, completely separate from CLI's node_modules
67
+ const script = `
68
+ // Suppress library deprecation warnings (e.g., body-parser)
69
+ process.noDeprecation = true;
70
+
71
+ const { createDevServer } = await import('@hubspot/cms-dev-server');
72
+ const { walk } = await import('@hubspot/local-dev-lib/fs');
73
+ const { createIgnoreFilter } = await import('@hubspot/local-dev-lib/ignoreRules');
74
+ const { isAllowedExtension } = await import('@hubspot/local-dev-lib/path');
75
+ const { FILE_UPLOAD_RESULT_TYPES } = await import('@hubspot/local-dev-lib/constants/files');
76
+ const cliProgress = (await import('cli-progress')).default;
77
+
78
+ ${dest
79
+ ? `
80
+ // Get uploadable files for preview
81
+ let filePaths = [];
82
+ try {
83
+ filePaths = await walk(${JSON.stringify(absoluteSrc)});
84
+ } catch (e) {
85
+ console.error('Error walking directory:', e);
86
+ }
87
+ filePaths = filePaths
88
+ .filter(file => isAllowedExtension(file))
89
+ .filter(createIgnoreFilter(false));
90
+
91
+ // Create progress bar for initial upload
92
+ function startProgressBar(numFiles) {
93
+ const initialUploadProgressBar = new cliProgress.SingleBar(
94
+ {
95
+ gracefulExit: true,
96
+ format: '[{bar}] {percentage}% | {value}/{total} | {label}',
97
+ hideCursor: true,
98
+ },
99
+ cliProgress.Presets.rect
100
+ );
101
+ initialUploadProgressBar.start(numFiles, 0, {
102
+ label: 'Preparing upload...',
103
+ });
104
+ let uploadsHaveStarted = false;
105
+ return {
106
+ onAttemptCallback: () => {},
107
+ onSuccessCallback: () => {
108
+ initialUploadProgressBar.increment();
109
+ if (!uploadsHaveStarted) {
110
+ uploadsHaveStarted = true;
111
+ initialUploadProgressBar.update(0, {
112
+ label: 'Uploading files...',
113
+ });
114
+ }
115
+ },
116
+ onFirstErrorCallback: () => {},
117
+ onRetryCallback: () => {},
118
+ onFinalErrorCallback: () => initialUploadProgressBar.increment(),
119
+ onFinishCallback: (results) => {
120
+ initialUploadProgressBar.update(numFiles, {
121
+ label: 'Upload complete',
122
+ });
123
+ initialUploadProgressBar.stop();
124
+ results.forEach(result => {
125
+ if (result.resultType == FILE_UPLOAD_RESULT_TYPES.FAILURE) {
126
+ console.error(\`Failed to upload \${result.file}\`);
127
+ }
128
+ });
129
+ },
130
+ };
131
+ }
132
+
133
+ const themePreviewOptions = {
134
+ filePaths,
135
+ startProgressBar,
136
+ resetSession: ${Boolean(resetSession)},
137
+ dest: ${JSON.stringify(dest)},
138
+ };
139
+ `
140
+ : 'const themePreviewOptions = undefined;'}
141
+
142
+ createDevServer(
143
+ ${JSON.stringify(absoluteSrc)},
144
+ false, // storybook
145
+ '', // config (uses env var HUBSPOT_CONFIG_PATH)
146
+ ${JSON.stringify(accountName || '')},
147
+ ${!noSsl}, // sslEnabled
148
+ ${Boolean(generateFieldsTypes)}, // fieldGenEnabled
149
+ themePreviewOptions
150
+ );
151
+ `;
152
+ // Set environment variables
153
+ const env = { ...process.env };
154
+ if (port) {
155
+ env.PORT = port.toString();
156
+ }
157
+ // Suppress Node.js deprecation warnings
158
+ env.NODE_NO_WARNINGS = '1';
159
+ // Set config path so cms-dev-server can find hubspot.config.yaml
160
+ // Use existing env var if set, otherwise get the default config path
161
+ if (!env.HUBSPOT_CONFIG_PATH) {
162
+ try {
163
+ env.HUBSPOT_CONFIG_PATH = getConfigFilePath();
164
+ }
165
+ catch (e) {
166
+ // Config file doesn't exist - cms-dev-server will handle this gracefully
167
+ }
168
+ }
169
+ console.log(env.HUBSPOT_CONFIG_PATH);
170
+ // Spawn Node with the inline script from the isolated cache directory
171
+ // This ensures complete isolation from CLI's React 19
172
+ const devServer = spawn('node', ['--input-type=module', '-e', script], {
173
+ stdio: 'inherit',
174
+ env,
175
+ cwd: cacheDir,
176
+ });
177
+ // Handle process events
178
+ devServer.on('error', error => {
179
+ console.error('Failed to start dev server:', error);
180
+ process.exit(1);
181
+ });
182
+ devServer.on('exit', (code, signal) => {
183
+ if (code !== 0 && code !== null) {
184
+ console.error(`Dev server exited with code ${code}`);
185
+ process.exit(code);
186
+ }
187
+ if (signal) {
188
+ console.error(`Dev server killed with signal ${signal}`);
189
+ process.exit(1);
190
+ }
191
+ });
192
+ // Handle CLI termination
193
+ process.on('SIGINT', () => {
194
+ devServer.kill('SIGINT');
195
+ });
196
+ process.on('SIGTERM', () => {
197
+ devServer.kill('SIGTERM');
198
+ });
199
+ return devServer;
200
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "8.0.9-experimental.0",
3
+ "version": "8.0.10-experimental.1",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
@@ -30,7 +30,6 @@
30
30
  "open": "7.4.2",
31
31
  "p-queue": "8.1.0",
32
32
  "react": "19.2.3",
33
- "react-dom": "19.2.3",
34
33
  "strip-ansi": "7.1.0",
35
34
  "table": "6.9.0",
36
35
  "tmp": "0.2.4",
@@ -120,7 +119,6 @@
120
119
  "registry": "https://registry.npmjs.org/"
121
120
  },
122
121
  "resolutions": {
123
- "eslint-visitor-keys": "4.2.0",
124
- "@hubspot/cms-dev-server/react": "18.3.1"
122
+ "eslint-visitor-keys": "4.2.0"
125
123
  }
126
124
  }