@hubspot/ui-extensions-dev-server 0.8.3 → 0.8.4

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,6 +1,7 @@
1
1
  import type { ExtensionConfig, PlatformVersion, ProjectComponentMap } from './types';
2
2
  import type { ServiceConfiguration } from '@hubspot/app-functions-dev-server/dist/types';
3
3
  import { PromptModule } from 'inquirer';
4
+ import { DevServerState } from './DevServerState';
4
5
  interface SetupArguments {
5
6
  components: ProjectComponentMap;
6
7
  debug?: boolean;
@@ -24,13 +25,10 @@ interface AppExtensionMapping {
24
25
  value: ExtensionConfig;
25
26
  }
26
27
  declare class DevModeInterface {
27
- config?: ExtensionConfig;
28
- appName?: string;
29
- title?: string;
30
- extensionConfigPath?: string;
28
+ configs?: ExtensionConfig[];
29
+ devServerState?: DevServerState;
31
30
  onUploadRequired?: VoidFunction;
32
31
  shutdown?: () => Promise<void>;
33
- _setDataFromExtensionConfig(extensionConfig: ExtensionConfig): void;
34
32
  _generateAppExtensionMappings(components: ProjectComponentMap): AppExtensionMapping[];
35
33
  _getPlatformVersion(projectConfig?: ProjectConfig): PlatformVersion;
36
34
  setup({ components, debug, extensionConfig, onUploadRequired, promptUser, }: SetupArguments): Promise<void>;
@@ -10,24 +10,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  step((generator = generator.apply(thisArg, _arguments || [])).next());
11
11
  });
12
12
  };
13
- var __importDefault = (this && this.__importDefault) || function (mod) {
14
- return (mod && mod.__esModule) ? mod : { "default": mod };
15
- };
16
13
  Object.defineProperty(exports, "__esModule", { value: true });
17
14
  const dev_1 = require("./dev");
18
- const path_1 = __importDefault(require("path"));
19
15
  const constants_1 = require("./constants");
20
16
  const config_1 = require("./config");
21
17
  // @ts-expect-error no type defs
22
18
  const logger_1 = require("@hubspot/cli-lib/logger");
19
+ const DevServerState_1 = require("./DevServerState");
23
20
  const utils_1 = require("./utils");
24
21
  class DevModeInterface {
25
- _setDataFromExtensionConfig(extensionConfig) {
26
- this.config = extensionConfig;
27
- this.extensionConfigPath = extensionConfig.extensionConfigPath;
28
- this.appName = extensionConfig.data.appName;
29
- this.title = extensionConfig.data.title;
30
- }
31
22
  _generateAppExtensionMappings(components) {
32
23
  // Loop over all of the app configs that are passed in
33
24
  const allComponentNames = Object.keys(components);
@@ -70,7 +61,7 @@ class DevModeInterface {
70
61
  (0, logger_1.setLogLevel)(debug ? logger_1.LOG_LEVEL.DEBUG : logger_1.LOG_LEVEL.LOG);
71
62
  this.onUploadRequired = onUploadRequired;
72
63
  if (extensionConfig) {
73
- this._setDataFromExtensionConfig(extensionConfig);
64
+ this.configs = [extensionConfig];
74
65
  return;
75
66
  }
76
67
  const choices = this._generateAppExtensionMappings(components);
@@ -78,16 +69,22 @@ class DevModeInterface {
78
69
  throw new Error('No extensions to run');
79
70
  }
80
71
  else if (choices.length === 1) {
81
- this._setDataFromExtensionConfig(choices[0].value);
72
+ this.configs = [choices[0].value];
82
73
  }
83
74
  else {
84
75
  const answers = yield promptUser({
85
- type: 'list',
86
- name: 'extension',
87
- message: 'Which extension would you like to run?',
76
+ type: 'checkbox',
77
+ name: 'extensions',
78
+ message: 'Which extension(s) would you like to run?',
79
+ validate: input => {
80
+ if (!input || input.length === 0) {
81
+ return 'Select at least one extension to run';
82
+ }
83
+ return true;
84
+ },
88
85
  choices,
89
86
  });
90
- this._setDataFromExtensionConfig(answers.extension);
87
+ this.configs = answers.extensions;
91
88
  }
92
89
  });
93
90
  }
@@ -95,9 +92,11 @@ class DevModeInterface {
95
92
  // eslint-disable-next-line require-await
96
93
  fileChange(filePath, __event) {
97
94
  return __awaiter(this, void 0, void 0, function* () {
98
- if (this.extensionConfigPath &&
99
- this.extensionConfigPath === filePath &&
100
- this.onUploadRequired) {
95
+ if (!this.devServerState || !this.devServerState.extensionsMetadata) {
96
+ return;
97
+ }
98
+ const relevantConfigFileChanged = this.devServerState.extensionsMetadata.some(metadata => metadata.config.extensionConfigPath === filePath);
99
+ if (relevantConfigFileChanged && this.onUploadRequired) {
101
100
  this.onUploadRequired();
102
101
  }
103
102
  });
@@ -107,24 +106,17 @@ class DevModeInterface {
107
106
  if (debug !== undefined) {
108
107
  (0, logger_1.setLogLevel)(debug ? logger_1.LOG_LEVEL.DEBUG : logger_1.LOG_LEVEL.LOG);
109
108
  }
110
- if (!this.config || !this.config.path) {
111
- throw new Error('Unable to load the required extension configuration files');
112
- }
113
- const appPath = this.config.path;
114
- // Pass options from the CLI for running app functions locally
115
- const functionsConfig = {
116
- app: { path: appPath },
109
+ this.devServerState = new DevServerState_1.DevServerState({
110
+ extensionConfigs: this.configs,
117
111
  accountId,
118
112
  httpClient,
119
113
  platformVersion: this._getPlatformVersion(projectConfig),
120
- };
121
- this.shutdown = yield (0, dev_1.startDevMode)({
122
- extensionConfig: this.config,
123
- outputDir: path_1.default.join(this.config.extensionPath, constants_1.OUTPUT_DIR),
124
- functionsConfig,
125
- root: appPath,
126
114
  });
127
- logger_1.logger.info(`Running extension '${this.title}' from app '${this.appName}'`);
115
+ this.shutdown = yield (0, dev_1.startDevMode)(this.devServerState);
116
+ this.devServerState.extensionsMetadata.forEach(metadata => {
117
+ const { config: { data: { title, appName }, }, } = metadata;
118
+ logger_1.logger.info(`Running extension '${title}' from app '${appName}'`);
119
+ });
128
120
  });
129
121
  }
130
122
  cleanup() {
@@ -0,0 +1,28 @@
1
+ import { ExtensionConfig, ExtensionMetadata, PlatformVersion } from './types';
2
+ import { ServiceConfiguration } from '@hubspot/app-functions-dev-server/dist/types';
3
+ interface DevServerStateArgs {
4
+ extensionConfigs?: ExtensionConfig[];
5
+ accountId: number | undefined;
6
+ httpClient: ServiceConfiguration['httpClient'] | undefined;
7
+ expressPort?: number;
8
+ webSocketPort?: number;
9
+ platformVersion: PlatformVersion;
10
+ }
11
+ export declare class DevServerState {
12
+ private _webSocketPort;
13
+ private _expressPort;
14
+ private _functionsConfig;
15
+ private _outputDir;
16
+ private _appPath;
17
+ private _extensionsMetadata;
18
+ private _portalId?;
19
+ constructor({ extensionConfigs, accountId, httpClient, expressPort, webSocketPort, platformVersion, }: DevServerStateArgs);
20
+ get portalId(): number | undefined;
21
+ get webSocketPort(): number;
22
+ get expressPort(): number;
23
+ get extensionsMetadata(): ExtensionMetadata[];
24
+ get functionsConfig(): Partial<ServiceConfiguration>;
25
+ get outputDir(): string;
26
+ get appPath(): string;
27
+ }
28
+ export {};
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DevServerState = void 0;
7
+ const constants_1 = require("./constants");
8
+ const path_1 = __importDefault(require("path"));
9
+ class DevServerState {
10
+ constructor({ extensionConfigs, accountId, httpClient, expressPort = constants_1.VITE_DEFAULT_PORT, webSocketPort = constants_1.WEBSOCKET_PORT, platformVersion, }) {
11
+ this._webSocketPort = constants_1.WEBSOCKET_PORT;
12
+ this._expressPort = constants_1.VITE_DEFAULT_PORT;
13
+ if (!extensionConfigs) {
14
+ throw new Error('Unable to load the required extension configuration files');
15
+ }
16
+ const extensionsMetadata = [];
17
+ extensionConfigs.forEach(config => {
18
+ const { appName, title, sourceId } = config.data;
19
+ extensionsMetadata.push({
20
+ config,
21
+ baseMessage: {
22
+ appName,
23
+ title,
24
+ sourceId,
25
+ callback: `http://hslocal.net:${expressPort}/${config.output}`,
26
+ portalId: accountId,
27
+ },
28
+ });
29
+ });
30
+ this._webSocketPort = webSocketPort;
31
+ this._expressPort = expressPort;
32
+ this._extensionsMetadata = extensionsMetadata;
33
+ this._appPath = extensionConfigs[0].path;
34
+ this._portalId = accountId;
35
+ this._outputDir = path_1.default.join(this._appPath, constants_1.OUTPUT_DIR);
36
+ // Pass options from the CLI for running app functions locally
37
+ this._functionsConfig = {
38
+ app: { path: this._appPath },
39
+ accountId,
40
+ httpClient,
41
+ platformVersion,
42
+ };
43
+ Object.freeze(this);
44
+ }
45
+ get portalId() {
46
+ return this._portalId;
47
+ }
48
+ get webSocketPort() {
49
+ return this._webSocketPort;
50
+ }
51
+ get expressPort() {
52
+ return this._expressPort;
53
+ }
54
+ get extensionsMetadata() {
55
+ return this._extensionsMetadata;
56
+ }
57
+ get functionsConfig() {
58
+ return this._functionsConfig;
59
+ }
60
+ get outputDir() {
61
+ return this._outputDir;
62
+ }
63
+ get appPath() {
64
+ return this._appPath;
65
+ }
66
+ }
67
+ exports.DevServerState = DevServerState;
package/dist/lib/dev.d.ts CHANGED
@@ -1,12 +1,2 @@
1
- import { ExtensionConfig } from './types';
2
- import { ServiceConfiguration } from '@hubspot/app-functions-dev-server/dist/types';
3
- interface StartDevModeArgs {
4
- outputDir?: string;
5
- extensionConfig: ExtensionConfig;
6
- webSocketPort?: number;
7
- expressPort?: number;
8
- root?: string;
9
- functionsConfig: Partial<ServiceConfiguration>;
10
- }
11
- export declare function startDevMode({ extensionConfig, functionsConfig, outputDir, expressPort, webSocketPort, root, }: StartDevModeArgs): Promise<() => Promise<void>>;
12
- export {};
1
+ import { DevServerState } from './DevServerState';
2
+ export declare function startDevMode(devServerState: DevServerState): Promise<() => Promise<void>>;
package/dist/lib/dev.js CHANGED
@@ -17,26 +17,25 @@ const vite_1 = require("vite");
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const server_1 = __importDefault(require("./server"));
19
19
  const devBuildPlugin_1 = __importDefault(require("./plugins/devBuildPlugin"));
20
- const constants_1 = require("./constants");
21
20
  // @ts-expect-error no type defs
22
21
  const logger_1 = require("@hubspot/cli-lib/logger");
23
22
  // @ts-expect-error no type defs
24
23
  const detect_port_1 = __importDefault(require("detect-port"));
25
- function _createViteDevServer({ outputDir, extensionConfig, webSocketPort, baseMessage, root, }) {
24
+ function _createViteDevServer(devServerState) {
26
25
  return __awaiter(this, void 0, void 0, function* () {
27
26
  return yield (0, vite_1.createServer)({
28
- root,
27
+ root: devServerState.appPath,
29
28
  logLevel: 'silent',
30
29
  appType: 'custom',
31
30
  mode: 'development',
32
31
  server: {
33
32
  middlewareMode: true,
34
33
  hmr: {
35
- port: webSocketPort,
34
+ port: devServerState.webSocketPort,
36
35
  },
37
36
  watch: {
38
37
  ignored: [
39
- path_1.default.join(outputDir, '/**/*'),
38
+ path_1.default.join(devServerState.outputDir, '/**/*'),
40
39
  '**/src/app/app.functions/**/*',
41
40
  '**/app.json',
42
41
  '**/package.json',
@@ -46,9 +45,7 @@ function _createViteDevServer({ outputDir, extensionConfig, webSocketPort, baseM
46
45
  },
47
46
  plugins: [
48
47
  (0, devBuildPlugin_1.default)({
49
- extensionConfig,
50
- outputDir,
51
- baseMessage,
48
+ devServerState,
52
49
  }),
53
50
  ],
54
51
  clearScreen: false,
@@ -64,38 +61,21 @@ function throwIfPortTaken(port) {
64
61
  }
65
62
  });
66
63
  }
67
- function startDevMode({ extensionConfig, functionsConfig, outputDir = constants_1.OUTPUT_DIR, expressPort = constants_1.VITE_DEFAULT_PORT, webSocketPort = constants_1.WEBSOCKET_PORT, root = process.cwd(), }) {
64
+ function startDevMode(devServerState) {
68
65
  return __awaiter(this, void 0, void 0, function* () {
69
- if (!extensionConfig) {
66
+ if (!devServerState || !devServerState.extensionsMetadata) {
70
67
  throw new Error('Unable to determine which extension to run');
71
68
  }
69
+ const { expressPort, webSocketPort } = devServerState;
72
70
  yield throwIfPortTaken(expressPort);
73
71
  const actualWebSocketPort = yield (0, detect_port_1.default)(webSocketPort);
74
72
  if (actualWebSocketPort !== webSocketPort) {
75
73
  logger_1.logger.debug(`WebSocket port ${webSocketPort} is in use; using next available port ${actualWebSocketPort}`);
76
74
  }
77
- const { appName, title, sourceId } = extensionConfig.data;
78
- const baseMessage = Object.freeze({
79
- appName,
80
- title,
81
- sourceId,
82
- callback: `http://hslocal.net:${expressPort}/${extensionConfig.output}`,
83
- portalId: functionsConfig.accountId,
84
- });
85
- const viteDevServer = yield _createViteDevServer({
86
- outputDir,
87
- extensionConfig,
88
- webSocketPort: actualWebSocketPort,
89
- baseMessage,
90
- root,
91
- });
75
+ const viteDevServer = yield _createViteDevServer(devServerState);
92
76
  const shutdownServer = yield (0, server_1.default)({
93
- outputDir,
94
- expressPort,
95
- webSocketPort: actualWebSocketPort,
96
- baseMessage,
77
+ devServerState,
97
78
  viteDevServer,
98
- functionsConfig,
99
79
  });
100
80
  return shutdownServer;
101
81
  });
@@ -1,12 +1,12 @@
1
- import { BaseMessage } from './types';
2
1
  import { Response } from 'express/lib/response';
3
2
  import { Request } from 'express/lib/request';
4
3
  import { Application } from 'express/lib/application';
4
+ import { DevServerState } from './DevServerState';
5
5
  declare class ExtensionsService {
6
6
  endpoint: string;
7
7
  constructor();
8
- add(server: Application, webSocketPort: number, outputDir: string, baseMessage: BaseMessage, capabilities: string[]): string[];
9
- generateExtensionsHandler(baseMessage: BaseMessage, webSocketPort: number, outputDir: string, capabilities?: string[]): (_req: Request, res: Response) => void;
8
+ add(server: Application, devServerState: DevServerState, capabilities: string[]): string[];
9
+ generateExtensionsHandler(devServerState: DevServerState, capabilities?: string[]): (_req: Request, res: Response) => void;
10
10
  }
11
11
  declare const _default: ExtensionsService;
12
12
  export default _default;
@@ -3,6 +3,7 @@ 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
+ /* eslint-disable hubspot-dev/no-unsupported-ts-syntax */
6
7
  const path_1 = __importDefault(require("path"));
7
8
  const constants_1 = require("./constants");
8
9
  const utils_1 = require("./utils");
@@ -10,22 +11,24 @@ class ExtensionsService {
10
11
  constructor() {
11
12
  this.endpoint = '/extensions';
12
13
  }
13
- add(server, webSocketPort, outputDir, baseMessage, capabilities) {
14
- server.get(this.endpoint, this.generateExtensionsHandler(baseMessage, webSocketPort, outputDir, capabilities));
14
+ add(server, devServerState, capabilities) {
15
+ server.get(this.endpoint, this.generateExtensionsHandler(devServerState, capabilities));
15
16
  return [this.endpoint];
16
17
  }
17
- generateExtensionsHandler(baseMessage, webSocketPort, outputDir, capabilities = []) {
18
+ generateExtensionsHandler(devServerState, capabilities = []) {
18
19
  return function extensionsHandler(_req, res) {
19
20
  try {
20
- const output = path_1.default.parse(baseMessage.callback).name;
21
+ const extensions = devServerState.extensionsMetadata.map(metadata => {
22
+ const { baseMessage } = metadata;
23
+ const output = path_1.default.parse(baseMessage.callback).name;
24
+ return Object.assign(Object.assign({}, baseMessage), { manifest: (0, utils_1.loadManifest)(devServerState.outputDir, output) });
25
+ });
21
26
  const response = {
22
- websocket: `ws://localhost:${webSocketPort}`,
27
+ websocket: `ws://localhost:${devServerState.webSocketPort}`,
23
28
  version: constants_1.EXTENSIONS_MESSAGE_VERSION,
24
29
  capabilities,
25
- portalId: baseMessage.portalId,
26
- extensions: [
27
- Object.assign(Object.assign({}, baseMessage), { manifest: (0, utils_1.loadManifest)(outputDir, output) }),
28
- ],
30
+ portalId: devServerState.portalId,
31
+ extensions,
29
32
  };
30
33
  res.status(200).json(response);
31
34
  }
@@ -14,7 +14,8 @@ const codeCheckingPlugin = options => {
14
14
  writeBundle(__options, __bundle) {
15
15
  try {
16
16
  const code = fs_1.default.readFileSync(output).toString();
17
- if (!code.includes('const extend = (...args) => self.extend(...args);')) {
17
+ if (!code.includes('const extend = (...args) => self.extend(...args);') &&
18
+ !code.includes('self.extend_V2(renderExtensionCallback)')) {
18
19
  logger_1.logger.warn('Unable to determine if your extension entry point is calling hubspot.extend, this may prevent it from rendering as expected');
19
20
  }
20
21
  }
@@ -1,9 +1,7 @@
1
1
  import Vite from 'vite';
2
- import { ExtensionConfig, BaseMessage } from '../types';
2
+ import { DevServerState } from '../DevServerState';
3
3
  export interface DevBuildPluginOptions {
4
- extensionConfig: ExtensionConfig;
5
- outputDir: string;
6
- baseMessage: BaseMessage;
4
+ devServerState: DevServerState;
7
5
  }
8
6
  export type DevBuildPlugin = (options: DevBuildPluginOptions) => Vite.Plugin;
9
7
  declare const devBuildPlugin: DevBuildPlugin;
@@ -47,16 +47,18 @@ const path_1 = __importDefault(require("path"));
47
47
  const logger_1 = require("@hubspot/cli-lib/logger");
48
48
  const friendlyLoggingPlugin_1 = __importDefault(require("./friendlyLoggingPlugin"));
49
49
  const relevantModulesPlugin_1 = __importStar(require("./relevantModulesPlugin"));
50
+ function addVersionToBaseMessage(baseMessage) {
51
+ return Object.assign(Object.assign({}, baseMessage), { version: constants_1.WEBSOCKET_MESSAGE_VERSION });
52
+ }
50
53
  const devBuildPlugin = options => {
51
- let lastBuildError;
52
- const { extensionConfig, outputDir, baseMessage } = options;
53
- const versionedBaseMessage = Object.assign(Object.assign({}, baseMessage), { version: constants_1.WEBSOCKET_MESSAGE_VERSION });
54
+ const { devServerState } = options;
55
+ let lastBuildErrorContext;
54
56
  const handleBuildError = (error, server) => {
55
- const { plugin, errors, frame, loc, id } = error;
57
+ const { error: { plugin, errors, frame, loc, id }, extensionMetadata, } = error;
56
58
  // Filter out our custom plugins, but send everything else
57
59
  if (!(plugin === null || plugin === void 0 ? void 0 : plugin.startsWith('ui-extensions'))) {
58
60
  // @ts-expect-error Our websocket messages don't match Vite format
59
- server.ws.send(Object.assign(Object.assign({}, versionedBaseMessage), { event: 'error', error: {
61
+ server.ws.send(Object.assign(Object.assign({}, addVersionToBaseMessage(extensionMetadata.baseMessage)), { event: 'error', error: {
60
62
  details: {
61
63
  errors,
62
64
  formattedError: (0, utils_1.stripAnsiColorCodes)(frame),
@@ -66,8 +68,9 @@ const devBuildPlugin = options => {
66
68
  } }));
67
69
  }
68
70
  };
69
- const devBuild = (server) => __awaiter(void 0, void 0, void 0, function* () {
71
+ const devBuild = (server, extensionMetadata, emptyOutDir = false) => __awaiter(void 0, void 0, void 0, function* () {
70
72
  try {
73
+ const { config: extensionConfig } = extensionMetadata;
71
74
  yield (0, vite_1.build)({
72
75
  logLevel: 'warn',
73
76
  mode: 'development',
@@ -95,24 +98,27 @@ const devBuildPlugin = options => {
95
98
  extensionPath: extensionConfig.extensionPath,
96
99
  }),
97
100
  (0, codeCheckingPlugin_1.default)({
98
- output: path_1.default.join(outputDir, extensionConfig.output),
101
+ output: path_1.default.join(devServerState.outputDir, extensionConfig.output),
99
102
  }),
100
103
  (0, friendlyLoggingPlugin_1.default)(),
101
104
  (0, relevantModulesPlugin_1.default)({ output: extensionConfig.output }),
102
105
  ], output: Object.assign(Object.assign({}, constants_1.ROLLUP_OPTIONS.output), { sourcemap: 'inline' }) }),
103
- outDir: outputDir,
104
- emptyOutDir: true,
106
+ outDir: devServerState.outputDir,
107
+ emptyOutDir,
105
108
  minify: false,
106
109
  },
107
110
  clearScreen: false,
108
111
  });
109
- lastBuildError = null;
112
+ lastBuildErrorContext = null;
110
113
  return true;
111
114
  }
112
115
  catch (error) {
113
- lastBuildError = error;
116
+ lastBuildErrorContext = {
117
+ error: error,
118
+ extensionMetadata,
119
+ };
114
120
  logger_1.logger.debug(error);
115
- handleBuildError(lastBuildError, server);
121
+ handleBuildError(lastBuildErrorContext, server);
116
122
  return false;
117
123
  }
118
124
  });
@@ -126,41 +132,52 @@ const devBuildPlugin = options => {
126
132
  localServer = server;
127
133
  localServer.ws.on('connection', () => {
128
134
  logger_1.logger.info('Browser connected and listening for bundle updates');
129
- // @ts-expect-error Our websocket messages don't match Vite format
130
- localServer.ws.send(Object.assign(Object.assign({}, versionedBaseMessage), { event: 'start' }));
131
- if (lastBuildError) {
132
- handleBuildError(lastBuildError, server);
135
+ devServerState.extensionsMetadata.forEach(metadata => {
136
+ // @ts-expect-error Our websocket messages don't match Vite format
137
+ localServer.ws.send(Object.assign(Object.assign({}, addVersionToBaseMessage(metadata.baseMessage)), { event: 'start' }));
138
+ });
139
+ if (lastBuildErrorContext) {
140
+ handleBuildError(lastBuildErrorContext, server);
133
141
  }
134
142
  });
135
- yield devBuild(localServer);
143
+ for (let i = 0; i < devServerState.extensionsMetadata.length; ++i) {
144
+ yield devBuild(localServer, devServerState.extensionsMetadata[i], i === 0);
145
+ }
136
146
  }),
137
147
  handleHotUpdate: ({ file, server }) => __awaiter(void 0, void 0, void 0, function* () {
138
148
  // If the file is not in the relevantModules list, it's update is inconsequential
139
- if (!(0, relevantModulesPlugin_1.getRelevantModules)(extensionConfig.output).includes(file)) {
140
- return [];
141
- }
142
- const successful = yield devBuild(server);
143
- if (!successful) {
144
- return [];
145
- }
146
- logger_1.logger.info(`Extension ${extensionConfig.data.title} updated, compiled`);
147
- if (server.ws.clients.size === 0) {
148
- logger_1.logger.debug('Bundle updated, no browsers connected to notify');
149
- return [];
149
+ const extensionsToRebuild = devServerState.extensionsMetadata.filter(metadata => {
150
+ const { config } = metadata;
151
+ return (0, relevantModulesPlugin_1.getRelevantModules)(config.output).includes(file);
152
+ });
153
+ for (let i = 0; i < extensionsToRebuild.length; ++i) {
154
+ const toRebuild = extensionsToRebuild[i];
155
+ const successful = yield devBuild(server, toRebuild);
156
+ if (!successful) {
157
+ return [];
158
+ }
159
+ const { config: extensionConfig } = toRebuild;
160
+ logger_1.logger.info(`Extension ${extensionConfig.data.title} updated, compiled`);
161
+ if (server.ws.clients.size === 0) {
162
+ logger_1.logger.debug('Bundle updated, no browsers connected to notify');
163
+ return [];
164
+ }
165
+ logger_1.logger.debug('Bundle updated, notifying connected browsers');
166
+ // @ts-expect-error Our websocket messages don't match Vite format
167
+ server.ws.send(Object.assign(Object.assign({}, addVersionToBaseMessage(toRebuild.baseMessage)), { event: 'update' }));
150
168
  }
151
- logger_1.logger.debug('Bundle updated, notifying connected browsers');
152
- // @ts-expect-error Our websocket messages don't match Vite format
153
- server.ws.send(Object.assign(Object.assign({}, versionedBaseMessage), { event: 'update' }));
154
169
  return [];
155
170
  }),
156
171
  buildEnd(error) {
157
172
  if (error) {
158
173
  logger_1.logger.error(error);
159
174
  }
160
- logger_1.logger.debug('Sending shutdown message to connected browsers');
161
175
  if (localServer && localServer.ws) {
162
- // @ts-expect-error Our websocket messages don't match Vite format
163
- localServer.ws.send(Object.assign(Object.assign({}, versionedBaseMessage), { event: 'shutdown' }));
176
+ logger_1.logger.debug('Sending shutdown message to connected browsers');
177
+ devServerState.extensionsMetadata.forEach(metadata => {
178
+ // @ts-expect-error Our websocket messages don't match Vite format
179
+ localServer.ws.send(Object.assign(Object.assign({}, addVersionToBaseMessage(metadata.baseMessage)), { event: 'shutdown' }));
180
+ });
164
181
  }
165
182
  },
166
183
  };
@@ -1,13 +1,8 @@
1
- import { BaseMessage } from './types';
2
1
  import { ViteDevServer } from 'vite';
3
- import { ServiceConfiguration } from '@hubspot/app-functions-dev-server/dist/types';
2
+ import { DevServerState } from './DevServerState';
4
3
  interface StartDevServerArgs {
5
- outputDir: string;
6
- expressPort: number;
7
- webSocketPort: number;
8
- baseMessage: BaseMessage;
4
+ devServerState: DevServerState;
9
5
  viteDevServer: ViteDevServer;
10
- functionsConfig: Partial<ServiceConfiguration>;
11
6
  }
12
- declare function startDevServer({ outputDir, expressPort, webSocketPort, baseMessage, viteDevServer, functionsConfig, }: StartDevServerArgs): Promise<() => Promise<void>>;
7
+ declare function startDevServer({ devServerState, viteDevServer, }: StartDevServerArgs): Promise<() => Promise<void>>;
13
8
  export default startDevServer;
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ /* eslint-disable hubspot-dev/no-unsupported-ts-syntax */
3
+ /* eslint-disable no-unused-expressions */
2
4
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
5
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
6
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -30,15 +32,17 @@ function listen(app, port) {
30
32
  });
31
33
  });
32
34
  }
33
- function startDevServer({ outputDir, expressPort, webSocketPort, baseMessage, viteDevServer, functionsConfig, }) {
35
+ function startDevServer({ devServerState, viteDevServer, }) {
36
+ var _a;
34
37
  return __awaiter(this, void 0, void 0, function* () {
35
38
  const app = (0, express_1.default)();
36
39
  // Setup middleware
37
40
  app.use((0, cors_1.default)());
38
- app.use(express_1.default.static(outputDir));
39
- app.use('/api/crm-extensibility/execution/internal/v3', (0, app_functions_dev_server_1.AppFunctionExecutionService)(Object.assign(Object.assign({}, functionsConfig), { logger: logger_1.logger })));
40
- logger_1.logger.info(`Serving app functions locally (platform version ${functionsConfig.platformVersion})`);
41
- const endpointsAdded = extensionsService_1.default.add(app, webSocketPort, outputDir, baseMessage, constants_1.SERVER_CAPABILITIES);
41
+ app.use(express_1.default.static(devServerState.outputDir));
42
+ app.use('/api/crm-extensibility/execution/internal/v3', (0, app_functions_dev_server_1.AppFunctionExecutionService)(Object.assign(Object.assign({}, devServerState.functionsConfig), { logger: logger_1.logger })));
43
+ logger_1.logger.info(`Serving app functions locally (platform version ${devServerState.functionsConfig.platformVersion})`);
44
+ const endpointsAdded = extensionsService_1.default.add(app, devServerState, constants_1.SERVER_CAPABILITIES);
45
+ const { expressPort } = devServerState;
42
46
  endpointsAdded.forEach(endpoint => {
43
47
  logger_1.logger.debug(`Listening at http://hslocal.net:${expressPort}${endpoint}`);
44
48
  });
@@ -46,15 +50,18 @@ function startDevServer({ outputDir, expressPort, webSocketPort, baseMessage, vi
46
50
  app.use(viteDevServer.middlewares);
47
51
  let server;
48
52
  try {
49
- server = yield listen(app, expressPort);
53
+ server = yield listen(app, devServerState.expressPort);
50
54
  }
51
55
  catch (e) {
52
56
  if (e.code === 'EADDRINUSE') {
53
- throw new Error(`Port ${expressPort} is already in use.`);
57
+ throw new Error(`Port ${devServerState.expressPort} is already in use.`);
54
58
  }
55
59
  throw new Error(e);
56
60
  }
57
- logger_1.logger.debug(`Listening at ${baseMessage.callback}`);
61
+ (_a = devServerState.extensionsMetadata) === null || _a === void 0 ? void 0 : _a.forEach(metadata => {
62
+ const { baseMessage } = metadata;
63
+ logger_1.logger.debug(`Listening at ${baseMessage.callback}`);
64
+ });
58
65
  return function shutdown() {
59
66
  return __awaiter(this, void 0, void 0, function* () {
60
67
  yield viteDevServer.pluginContainer.close();
@@ -52,5 +52,9 @@ export interface BaseMessage {
52
52
  sourceId?: string | null;
53
53
  portalId?: number;
54
54
  }
55
+ export interface ExtensionMetadata {
56
+ baseMessage: BaseMessage;
57
+ config: ExtensionConfig;
58
+ }
55
59
  export type PlatformVersion = typeof PLATFORM_VERSION[keyof typeof PLATFORM_VERSION];
56
60
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions-dev-server",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -63,5 +63,5 @@
63
63
  "optional": true
64
64
  }
65
65
  },
66
- "gitHead": "ab25629916c542f19bf8cde83a10ad8bd225078f"
66
+ "gitHead": "41e3484343d34136e81540d774a101ccf99642b3"
67
67
  }