@hubspot/cli 4.2.1-beta.0 → 4.2.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1 -1
- package/commands/accounts/list.js +1 -1
- package/commands/project/add.js +1 -1
- package/commands/project/dev.js +91 -26
- package/commands/project/listBuilds.js +4 -1
- package/commands/project/upload.js +3 -0
- package/commands/project/watch.js +3 -0
- package/commands/project.js +9 -6
- package/commands/sandbox/create.js +10 -1
- package/commands/sandbox/delete.js +7 -11
- package/lang/en.lyaml +46 -16
- package/lib/DevServerManager.js +112 -72
- package/lib/LocalDevManager.js +15 -15
- package/lib/LocalDevManagerV2.js +239 -0
- package/lib/SpinniesManager.js +3 -0
- package/lib/projectStructure.js +106 -0
- package/lib/projects.js +32 -3
- package/lib/ui.js +12 -0
- package/lib/usageTracking.js +57 -0
- package/package.json +6 -5
package/lib/DevServerManager.js
CHANGED
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const bodyParser = require('body-parser');
|
|
3
3
|
const cors = require('cors');
|
|
4
|
-
const
|
|
4
|
+
const httpClient = require('@hubspot/cli-lib/http');
|
|
5
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
6
|
const { getProjectDetailUrl } = require('./projects');
|
|
7
|
+
const { COMPONENT_TYPES } = require('./projectStructure');
|
|
6
8
|
const { i18n } = require('./lang');
|
|
7
9
|
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
8
|
-
const {
|
|
10
|
+
const { promptUser } = require('./prompts/promptUtils');
|
|
9
11
|
|
|
10
12
|
const i18nKey = 'cli.lib.DevServerManager';
|
|
11
13
|
|
|
12
14
|
const DEFAULT_PORT = 8080;
|
|
15
|
+
const SERVER_KEYS = {
|
|
16
|
+
app: 'app',
|
|
17
|
+
};
|
|
13
18
|
|
|
14
19
|
class DevServerManager {
|
|
15
20
|
constructor() {
|
|
16
21
|
this.initialized = false;
|
|
22
|
+
this.started = false;
|
|
23
|
+
this.componentsByType = {};
|
|
17
24
|
this.server = null;
|
|
18
25
|
this.path = null;
|
|
19
26
|
this.devServers = {};
|
|
27
|
+
this.debug = false;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
safeLoadServer() {
|
|
23
31
|
try {
|
|
24
|
-
|
|
32
|
+
const { DevModeInterface } = require('@hubspot/ui-extensions-dev-server');
|
|
33
|
+
this.devServers[SERVER_KEYS.app] = {
|
|
34
|
+
componentType: COMPONENT_TYPES.app,
|
|
35
|
+
serverInterface: DevModeInterface,
|
|
36
|
+
};
|
|
25
37
|
} catch (e) {
|
|
26
|
-
logger.debug(
|
|
27
|
-
`Failed to load dev server interface at ${serverInterfacePath}`
|
|
28
|
-
);
|
|
38
|
+
logger.debug('Failed to load dev server interface: ', e);
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -34,8 +44,16 @@ class DevServerManager {
|
|
|
34
44
|
|
|
35
45
|
for (let i = 0; i < serverKeys.length; i++) {
|
|
36
46
|
const serverKey = serverKeys[i];
|
|
37
|
-
const
|
|
38
|
-
|
|
47
|
+
const devServer = this.devServers[serverKey];
|
|
48
|
+
|
|
49
|
+
const compatibleComponents =
|
|
50
|
+
this.componentsByType[devServer.componentType] || {};
|
|
51
|
+
|
|
52
|
+
if (Object.keys(compatibleComponents).length) {
|
|
53
|
+
await callback(devServer.serverInterface, compatibleComponents);
|
|
54
|
+
} else {
|
|
55
|
+
logger.debug(i18n(`${i18nKey}.noCompatibleComponents`, { serverKey }));
|
|
56
|
+
}
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
|
|
@@ -43,82 +61,104 @@ class DevServerManager {
|
|
|
43
61
|
return this.path ? `${this.path}/${path}` : null;
|
|
44
62
|
}
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
arrangeComponentsByType(components) {
|
|
65
|
+
return components.reduce((acc, component) => {
|
|
66
|
+
if (!acc[component.type]) {
|
|
67
|
+
acc[component.type] = {};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
acc[component.type][component.config.name] = component;
|
|
71
|
+
|
|
72
|
+
return acc;
|
|
73
|
+
}, {});
|
|
54
74
|
}
|
|
55
75
|
|
|
56
|
-
async
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
spinniesLogger,
|
|
63
|
-
}) {
|
|
64
|
-
const app = express();
|
|
65
|
-
|
|
66
|
-
// Install Middleware
|
|
67
|
-
app.use(bodyParser.json({ limit: '50mb' }));
|
|
68
|
-
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
|
69
|
-
app.use(cors());
|
|
70
|
-
|
|
71
|
-
// Configure
|
|
72
|
-
app.set('trust proxy', true);
|
|
73
|
-
|
|
74
|
-
// Initialize a base route
|
|
75
|
-
app.get('/', (req, res) => {
|
|
76
|
-
res.send('HubSpot local dev server');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Initialize URL redirects
|
|
80
|
-
app.get('/hs/project', (req, res) => {
|
|
81
|
-
res.redirect(getProjectDetailUrl(projectConfig.name, accountId));
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Start server
|
|
85
|
-
this.server = await app.listen(DEFAULT_PORT).on('error', err => {
|
|
86
|
-
if (err.code === 'EADDRINUSE') {
|
|
87
|
-
logger.error(i18n(`${i18nKey}.portConflict`, { port: DEFAULT_PORT }));
|
|
88
|
-
logger.log();
|
|
89
|
-
process.exit(EXIT_CODES.ERROR);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const projectFiles = await walk(projectSourceDir);
|
|
94
|
-
|
|
95
|
-
// Initialize component servers
|
|
96
|
-
await this.iterateDevServers(async (serverInterface, serverKey) => {
|
|
97
|
-
if (serverInterface.start) {
|
|
98
|
-
await serverInterface.start({
|
|
99
|
-
debug,
|
|
100
|
-
extension,
|
|
101
|
-
logger: this.makeLogger(spinniesLogger, serverKey),
|
|
102
|
-
projectConfig,
|
|
103
|
-
projectFiles,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
});
|
|
76
|
+
async setup({ alpha, components, debug, onUploadRequired }) {
|
|
77
|
+
this.debug = debug;
|
|
78
|
+
|
|
79
|
+
this.componentsByType = this.arrangeComponentsByType(components);
|
|
80
|
+
|
|
81
|
+
this.safeLoadServer();
|
|
107
82
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
83
|
+
await this.iterateDevServers(
|
|
84
|
+
async (serverInterface, compatibleComponents) => {
|
|
85
|
+
if (serverInterface.setup) {
|
|
86
|
+
await serverInterface.setup({
|
|
87
|
+
alpha,
|
|
88
|
+
components: compatibleComponents,
|
|
89
|
+
debug,
|
|
90
|
+
onUploadRequired,
|
|
91
|
+
promptUser,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
111
96
|
|
|
112
97
|
this.initialized = true;
|
|
113
98
|
}
|
|
114
99
|
|
|
115
|
-
async
|
|
100
|
+
async start({ alpha, accountId, projectConfig }) {
|
|
116
101
|
if (this.initialized) {
|
|
102
|
+
const app = express();
|
|
103
|
+
|
|
104
|
+
// Install Middleware
|
|
105
|
+
app.use(bodyParser.json({ limit: '50mb' }));
|
|
106
|
+
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
|
107
|
+
app.use(cors());
|
|
108
|
+
|
|
109
|
+
// Configure
|
|
110
|
+
app.set('trust proxy', true);
|
|
111
|
+
|
|
112
|
+
// Initialize a base route
|
|
113
|
+
app.get('/', (req, res) => {
|
|
114
|
+
res.send('HubSpot local dev server');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Initialize URL redirects
|
|
118
|
+
app.get('/hs/project', (req, res) => {
|
|
119
|
+
res.redirect(getProjectDetailUrl(projectConfig.name, accountId));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Start server
|
|
123
|
+
this.server = await app.listen(DEFAULT_PORT).on('error', err => {
|
|
124
|
+
if (err.code === 'EADDRINUSE') {
|
|
125
|
+
logger.error(i18n(`${i18nKey}.portConflict`, { port: DEFAULT_PORT }));
|
|
126
|
+
logger.log();
|
|
127
|
+
process.exit(EXIT_CODES.ERROR);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Initialize component servers
|
|
132
|
+
await this.iterateDevServers(async serverInterface => {
|
|
133
|
+
if (serverInterface.start) {
|
|
134
|
+
await serverInterface.start({
|
|
135
|
+
alpha,
|
|
136
|
+
accountId,
|
|
137
|
+
debug: this.debug,
|
|
138
|
+
httpClient,
|
|
139
|
+
projectConfig,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this.path = this.server.address()
|
|
145
|
+
? `http://localhost:${this.server.address().port}`
|
|
146
|
+
: null;
|
|
147
|
+
} else {
|
|
148
|
+
throw new Error(i18n(`${i18nKey}.notInitialized`));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.started = true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async cleanup() {
|
|
155
|
+
if (this.started) {
|
|
117
156
|
await this.iterateDevServers(async serverInterface => {
|
|
118
157
|
if (serverInterface.cleanup) {
|
|
119
158
|
await serverInterface.cleanup();
|
|
120
159
|
}
|
|
121
160
|
});
|
|
161
|
+
|
|
122
162
|
if (this.server) {
|
|
123
163
|
await this.server.close();
|
|
124
164
|
}
|
package/lib/LocalDevManager.js
CHANGED
|
@@ -54,8 +54,6 @@ class LocalDevManager {
|
|
|
54
54
|
this.targetAccountId = options.targetAccountId;
|
|
55
55
|
this.projectConfig = options.projectConfig;
|
|
56
56
|
this.projectDir = options.projectDir;
|
|
57
|
-
this.extension = options.extension;
|
|
58
|
-
this.devServerPath = options.devServerPath;
|
|
59
57
|
this.uploadPermission =
|
|
60
58
|
options.uploadPermission || UPLOAD_PERMISSIONS.always;
|
|
61
59
|
this.debug = options.debug || false;
|
|
@@ -102,12 +100,8 @@ class LocalDevManager {
|
|
|
102
100
|
|
|
103
101
|
await this.devServerStart();
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
await this.startWatching();
|
|
108
|
-
} else {
|
|
109
|
-
this.uploadPermission = UPLOAD_PERMISSIONS.never;
|
|
110
|
-
}
|
|
103
|
+
this.uploadQueue.start();
|
|
104
|
+
await this.startWatching();
|
|
111
105
|
|
|
112
106
|
this.updateKeypressListeners();
|
|
113
107
|
|
|
@@ -309,7 +303,8 @@ class LocalDevManager {
|
|
|
309
303
|
try {
|
|
310
304
|
const { buildId } = await provisionBuild(
|
|
311
305
|
this.targetAccountId,
|
|
312
|
-
this.projectConfig.name
|
|
306
|
+
this.projectConfig.name,
|
|
307
|
+
this.projectConfig.platformVersion
|
|
313
308
|
);
|
|
314
309
|
this.currentStagedBuildId = buildId;
|
|
315
310
|
} catch (err) {
|
|
@@ -485,10 +480,12 @@ class LocalDevManager {
|
|
|
485
480
|
}),
|
|
486
481
|
status: 'non-spinnable',
|
|
487
482
|
});
|
|
483
|
+
const path =
|
|
484
|
+
event === WATCH_EVENTS.unlinkDir ? `${remotePath}/` : remotePath;
|
|
488
485
|
await deleteFileFromBuild(
|
|
489
486
|
this.targetAccountId,
|
|
490
487
|
this.projectConfig.name,
|
|
491
|
-
|
|
488
|
+
path
|
|
492
489
|
);
|
|
493
490
|
SpinniesManager.update(spinnerName, {
|
|
494
491
|
text: i18n(`${i18nKey}.upload.uploadedRemoveChange`, {
|
|
@@ -540,7 +537,11 @@ class LocalDevManager {
|
|
|
540
537
|
let queueBuildError;
|
|
541
538
|
|
|
542
539
|
try {
|
|
543
|
-
await queueBuild(
|
|
540
|
+
await queueBuild(
|
|
541
|
+
this.targetAccountId,
|
|
542
|
+
this.projectConfig.name,
|
|
543
|
+
this.projectConfig.platformVersion
|
|
544
|
+
);
|
|
544
545
|
} catch (err) {
|
|
545
546
|
queueBuildError = err;
|
|
546
547
|
}
|
|
@@ -667,13 +668,12 @@ class LocalDevManager {
|
|
|
667
668
|
|
|
668
669
|
async devServerStart() {
|
|
669
670
|
try {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
671
|
+
// Set this to true manually for now
|
|
672
|
+
DevServerManager.initialized = true;
|
|
673
|
+
|
|
673
674
|
await DevServerManager.start({
|
|
674
675
|
accountId: this.targetAccountId,
|
|
675
676
|
debug: this.debug,
|
|
676
|
-
extension: this.extension,
|
|
677
677
|
spinniesLogger: this.handleServerLog,
|
|
678
678
|
projectConfig: this.projectConfig,
|
|
679
679
|
projectSourceDir: this.projectSourceDir,
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { i18n } = require('./lang');
|
|
4
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
|
+
const { handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
6
|
+
const {
|
|
7
|
+
getAccountId,
|
|
8
|
+
getConfigDefaultAccount,
|
|
9
|
+
} = require('@hubspot/cli-lib/lib/config');
|
|
10
|
+
const SpinniesManager = require('./SpinniesManager');
|
|
11
|
+
const DevServerManager = require('./DevServerManager');
|
|
12
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
13
|
+
const { getProjectDetailUrl } = require('./projects');
|
|
14
|
+
const {
|
|
15
|
+
COMPONENT_TYPES,
|
|
16
|
+
findProjectComponents,
|
|
17
|
+
getAppCardConfigs,
|
|
18
|
+
} = require('./projectStructure');
|
|
19
|
+
const {
|
|
20
|
+
UI_COLORS,
|
|
21
|
+
uiAccountDescription,
|
|
22
|
+
uiBetaMessage,
|
|
23
|
+
uiLink,
|
|
24
|
+
uiLine,
|
|
25
|
+
} = require('./ui');
|
|
26
|
+
|
|
27
|
+
const i18nKey = 'cli.lib.LocalDevManagerV2';
|
|
28
|
+
|
|
29
|
+
class LocalDevManagerV2 {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.targetAccountId = options.targetAccountId;
|
|
32
|
+
this.projectConfig = options.projectConfig;
|
|
33
|
+
this.projectDir = options.projectDir;
|
|
34
|
+
this.debug = options.debug || false;
|
|
35
|
+
this.alpha = options.alpha;
|
|
36
|
+
this.deployedBuild = options.deployedBuild;
|
|
37
|
+
|
|
38
|
+
this.projectSourceDir = path.join(
|
|
39
|
+
this.projectDir,
|
|
40
|
+
this.projectConfig.srcDir
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!this.targetAccountId || !this.projectConfig || !this.projectDir) {
|
|
44
|
+
logger.log(i18n(`${i18nKey}.failedToInitialize`));
|
|
45
|
+
process.exit(EXIT_CODES.ERROR);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async start() {
|
|
50
|
+
SpinniesManager.removeAll();
|
|
51
|
+
SpinniesManager.init();
|
|
52
|
+
|
|
53
|
+
const components = await findProjectComponents(this.projectSourceDir);
|
|
54
|
+
|
|
55
|
+
if (!components.length) {
|
|
56
|
+
logger.log();
|
|
57
|
+
logger.error(i18n(`${i18nKey}.noComponents`));
|
|
58
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const runnableComponents = components.filter(
|
|
62
|
+
component => component.runnable
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!runnableComponents.length) {
|
|
66
|
+
logger.log();
|
|
67
|
+
logger.error(i18n(`${i18nKey}.noRunnableComponents`));
|
|
68
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logger.log();
|
|
72
|
+
const setupSucceeded = await this.devServerSetup(runnableComponents);
|
|
73
|
+
|
|
74
|
+
if (setupSucceeded || !this.debug) {
|
|
75
|
+
console.clear();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
uiBetaMessage(i18n(`${i18nKey}.betaMessage`));
|
|
79
|
+
logger.log();
|
|
80
|
+
logger.log(
|
|
81
|
+
chalk.hex(UI_COLORS.orange)(
|
|
82
|
+
i18n(`${i18nKey}.running`, {
|
|
83
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
84
|
+
projectName: this.projectConfig.name,
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
logger.log(
|
|
89
|
+
uiLink(
|
|
90
|
+
i18n(`${i18nKey}.viewInHubSpotLink`),
|
|
91
|
+
getProjectDetailUrl(this.projectConfig.name, this.targetAccountId)
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
logger.log();
|
|
95
|
+
logger.log(i18n(`${i18nKey}.quitHelper`));
|
|
96
|
+
uiLine();
|
|
97
|
+
logger.log();
|
|
98
|
+
|
|
99
|
+
await this.devServerStart();
|
|
100
|
+
|
|
101
|
+
this.updateKeypressListeners();
|
|
102
|
+
|
|
103
|
+
this.compareLocalProjectToDeployed(runnableComponents);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async stop() {
|
|
107
|
+
SpinniesManager.add('cleanupMessage', {
|
|
108
|
+
text: i18n(`${i18nKey}.exitingStart`),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const cleanupSucceeded = await this.devServerCleanup();
|
|
112
|
+
|
|
113
|
+
if (!cleanupSucceeded) {
|
|
114
|
+
SpinniesManager.fail('cleanupMessage', {
|
|
115
|
+
text: i18n(`${i18nKey}.exitingFail`),
|
|
116
|
+
});
|
|
117
|
+
process.exit(EXIT_CODES.ERROR);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
SpinniesManager.succeed('cleanupMessage', {
|
|
121
|
+
text: i18n(`${i18nKey}.exitingSucceed`),
|
|
122
|
+
});
|
|
123
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
updateKeypressListeners() {
|
|
127
|
+
handleKeypress(async key => {
|
|
128
|
+
if ((key.ctrl && key.name === 'c') || key.name === 'q') {
|
|
129
|
+
this.stop();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
logUploadWarning(reason) {
|
|
135
|
+
const currentDefaultAccount = getConfigDefaultAccount();
|
|
136
|
+
const defaultAccountId = getAccountId(currentDefaultAccount);
|
|
137
|
+
|
|
138
|
+
logger.log();
|
|
139
|
+
logger.warn(i18n(`${i18nKey}.uploadWarning.header`, { reason }));
|
|
140
|
+
logger.log(i18n(`${i18nKey}.uploadWarning.stopDev`));
|
|
141
|
+
if (this.targetAccountId !== defaultAccountId) {
|
|
142
|
+
logger.log(
|
|
143
|
+
i18n(`${i18nKey}.uploadWarning.runUploadWithAccount`, {
|
|
144
|
+
accountId: this.targetAccountId,
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
} else {
|
|
148
|
+
logger.log(i18n(`${i18nKey}.uploadWarning.runUpload`));
|
|
149
|
+
}
|
|
150
|
+
logger.log(i18n(`${i18nKey}.uploadWarning.restartDev`));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
compareLocalProjectToDeployed(runnableComponents) {
|
|
154
|
+
const deployedComponentNames = this.deployedBuild.subbuildStatuses.map(
|
|
155
|
+
subbuildStatus => subbuildStatus.buildName
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
let missingComponents = [];
|
|
159
|
+
|
|
160
|
+
runnableComponents.forEach(({ type, config, path }) => {
|
|
161
|
+
if (type === COMPONENT_TYPES.app) {
|
|
162
|
+
const cardConfigs = getAppCardConfigs(config, path);
|
|
163
|
+
|
|
164
|
+
cardConfigs.forEach(cardConfig => {
|
|
165
|
+
if (
|
|
166
|
+
cardConfig.data &&
|
|
167
|
+
cardConfig.data.title &&
|
|
168
|
+
!deployedComponentNames.includes(cardConfig.data.title)
|
|
169
|
+
) {
|
|
170
|
+
missingComponents.push(cardConfig.data.title);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (missingComponents.length) {
|
|
177
|
+
this.logUploadWarning(
|
|
178
|
+
i18n(`${i18nKey}.uploadWarning.missingComponents`, {
|
|
179
|
+
missingComponents: missingComponents.join(','),
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async devServerSetup(components) {
|
|
186
|
+
try {
|
|
187
|
+
await DevServerManager.setup({
|
|
188
|
+
alpha: this.alpha,
|
|
189
|
+
components,
|
|
190
|
+
debug: this.debug,
|
|
191
|
+
onUploadRequired: this.logUploadWarning.bind(this),
|
|
192
|
+
});
|
|
193
|
+
return true;
|
|
194
|
+
} catch (e) {
|
|
195
|
+
if (this.debug) {
|
|
196
|
+
logger.error(e);
|
|
197
|
+
}
|
|
198
|
+
logger.error(
|
|
199
|
+
i18n(`${i18nKey}.devServer.setupError`, { message: e.message })
|
|
200
|
+
);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async devServerStart() {
|
|
206
|
+
try {
|
|
207
|
+
await DevServerManager.start({
|
|
208
|
+
alpha: this.alpha,
|
|
209
|
+
accountId: this.targetAccountId,
|
|
210
|
+
projectConfig: this.projectConfig,
|
|
211
|
+
});
|
|
212
|
+
} catch (e) {
|
|
213
|
+
if (this.debug) {
|
|
214
|
+
logger.error(e);
|
|
215
|
+
}
|
|
216
|
+
logger.error(
|
|
217
|
+
i18n(`${i18nKey}.devServer.startError`, { message: e.message })
|
|
218
|
+
);
|
|
219
|
+
process.exit(EXIT_CODES.ERROR);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async devServerCleanup() {
|
|
224
|
+
try {
|
|
225
|
+
await DevServerManager.cleanup();
|
|
226
|
+
return true;
|
|
227
|
+
} catch (e) {
|
|
228
|
+
if (this.debug) {
|
|
229
|
+
logger.error(e);
|
|
230
|
+
}
|
|
231
|
+
logger.error(
|
|
232
|
+
i18n(`${i18nKey}.devServer.cleanupError`, { message: e.message })
|
|
233
|
+
);
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = LocalDevManagerV2;
|
package/lib/SpinniesManager.js
CHANGED
|
@@ -53,6 +53,9 @@ class SpinniesManager {
|
|
|
53
53
|
// Default Spinnies fields
|
|
54
54
|
this.spinners = {};
|
|
55
55
|
this.isCursorHidden = false;
|
|
56
|
+
if (this.currentInterval) {
|
|
57
|
+
clearInterval(this.currentInterval);
|
|
58
|
+
}
|
|
56
59
|
this.currentInterval = null;
|
|
57
60
|
this.stream = process.stderr;
|
|
58
61
|
this.lineCount = 0;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { walk } = require('@hubspot/cli-lib/lib/walk');
|
|
4
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
|
+
|
|
6
|
+
const COMPONENT_TYPES = Object.freeze({
|
|
7
|
+
app: 'app',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const APP_COMPONENT_CONFIG = 'app.json';
|
|
11
|
+
|
|
12
|
+
function loadConfigFile(configPath) {
|
|
13
|
+
if (configPath) {
|
|
14
|
+
try {
|
|
15
|
+
const source = fs.readFileSync(configPath);
|
|
16
|
+
const parsedConfig = JSON.parse(source);
|
|
17
|
+
return parsedConfig;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
logger.debug(e);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getAppCardConfigs(appConfig, appPath) {
|
|
26
|
+
let cardConfigs = [];
|
|
27
|
+
let cards;
|
|
28
|
+
|
|
29
|
+
if (appConfig && appConfig.extensions && appConfig.extensions.crm) {
|
|
30
|
+
cards = appConfig.extensions.crm.cards;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (cards) {
|
|
34
|
+
cards.forEach(({ file }) => {
|
|
35
|
+
const cardConfigPath = path.join(appPath, file);
|
|
36
|
+
const cardConfig = loadConfigFile(cardConfigPath);
|
|
37
|
+
|
|
38
|
+
if (cardConfig) {
|
|
39
|
+
cardConfigs.push(cardConfig);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return cardConfigs;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getIsLegacyApp(appConfig, appPath) {
|
|
48
|
+
const cardConfigs = getAppCardConfigs(appConfig, appPath);
|
|
49
|
+
|
|
50
|
+
if (!cardConfigs.length) {
|
|
51
|
+
// Assume any app that does not have any cards is not legacy
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let hasAnyReactExtensions = false;
|
|
56
|
+
|
|
57
|
+
cardConfigs.forEach(cardConfig => {
|
|
58
|
+
if (!hasAnyReactExtensions) {
|
|
59
|
+
const isReactExtension =
|
|
60
|
+
cardConfig &&
|
|
61
|
+
!!cardConfig.data &&
|
|
62
|
+
!!cardConfig.data.module &&
|
|
63
|
+
!!cardConfig.data.module.file;
|
|
64
|
+
|
|
65
|
+
hasAnyReactExtensions = isReactExtension;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return !hasAnyReactExtensions;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function findProjectComponents(projectSourceDir) {
|
|
73
|
+
const components = [];
|
|
74
|
+
|
|
75
|
+
const projectFiles = await walk(projectSourceDir);
|
|
76
|
+
|
|
77
|
+
projectFiles.forEach(projectFile => {
|
|
78
|
+
// Find app components
|
|
79
|
+
if (projectFile.endsWith(APP_COMPONENT_CONFIG)) {
|
|
80
|
+
const parsedAppConfig = loadConfigFile(projectFile);
|
|
81
|
+
|
|
82
|
+
if (parsedAppConfig && parsedAppConfig.name) {
|
|
83
|
+
const appPath = projectFile.substring(
|
|
84
|
+
0,
|
|
85
|
+
projectFile.indexOf(APP_COMPONENT_CONFIG)
|
|
86
|
+
);
|
|
87
|
+
const isLegacy = getIsLegacyApp(parsedAppConfig, appPath);
|
|
88
|
+
|
|
89
|
+
components.push({
|
|
90
|
+
type: COMPONENT_TYPES.app,
|
|
91
|
+
config: parsedAppConfig,
|
|
92
|
+
runnable: !isLegacy,
|
|
93
|
+
path: appPath,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return components;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
COMPONENT_TYPES,
|
|
104
|
+
findProjectComponents,
|
|
105
|
+
getAppCardConfigs,
|
|
106
|
+
};
|