@hubspot/ui-extensions-dev-server 0.4.0 → 0.6.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.
- package/cli/config.js +16 -24
- package/cli/logger.js +8 -8
- package/cli/run.js +26 -9
- package/cli/utils.js +5 -0
- package/lib/DevModeInterface.js +107 -43
- package/lib/build.js +6 -9
- package/lib/constants.js +7 -1
- package/lib/dev.js +12 -5
- package/lib/extensionsService.js +14 -3
- package/lib/plugins/codeInjectionPlugin.js +30 -0
- package/lib/plugins/devBuildPlugin.js +5 -1
- package/lib/self.js +4 -0
- package/lib/server.js +23 -7
- package/lib/utils.js +5 -2
- package/package.json +9 -5
- package/cli/extensions.js +0 -9
- package/cli/userInput.js +0 -33
package/cli/config.js
CHANGED
|
@@ -3,21 +3,9 @@ const path = require('path');
|
|
|
3
3
|
const { getUrlSafeFileName } = require('../lib/utils');
|
|
4
4
|
const { MAIN_APP_CONFIG } = require('../lib/constants');
|
|
5
5
|
|
|
6
|
-
function getAppConfigPath(projectFiles) {
|
|
7
|
-
return projectFiles.find(filePath => filePath.endsWith(MAIN_APP_CONFIG));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
6
|
function loadConfigByPath(configPath) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const source = fs.readFileSync(configPath);
|
|
14
|
-
const parsedConfig = JSON.parse(source);
|
|
15
|
-
return parsedConfig;
|
|
16
|
-
} catch (e) {
|
|
17
|
-
console.log(e);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
7
|
+
const source = fs.readFileSync(configPath);
|
|
8
|
+
return JSON.parse(source);
|
|
21
9
|
}
|
|
22
10
|
|
|
23
11
|
function loadExtensionConfig(appConfig, appPath) {
|
|
@@ -32,23 +20,28 @@ function loadExtensionConfig(appConfig, appPath) {
|
|
|
32
20
|
const cardConfig = loadConfigByPath(cardConfigPath);
|
|
33
21
|
|
|
34
22
|
if (cardConfig && cardConfig.data) {
|
|
35
|
-
const
|
|
23
|
+
const cardConfigDir = path.parse(cardConfigPath).dir;
|
|
36
24
|
|
|
37
25
|
const entryPointPath = path.join(
|
|
38
|
-
|
|
26
|
+
cardConfigDir,
|
|
39
27
|
cardConfig.data?.module?.file
|
|
40
28
|
);
|
|
41
29
|
|
|
42
30
|
cardConfig.data.module.file = entryPointPath;
|
|
43
31
|
|
|
44
|
-
outputConfig[entryPointPath] =
|
|
45
|
-
|
|
46
|
-
entryPointPath
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
outputConfig[entryPointPath] = {
|
|
33
|
+
...cardConfig,
|
|
34
|
+
output: getUrlSafeFileName(entryPointPath),
|
|
35
|
+
path: appPath,
|
|
36
|
+
extensionPath: path.parse(entryPointPath).dir,
|
|
37
|
+
data: {
|
|
38
|
+
...cardConfig.data,
|
|
39
|
+
appName: appConfig.name,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
49
42
|
}
|
|
50
43
|
} catch (e) {
|
|
51
|
-
|
|
44
|
+
throw new Error(`Unable to load ${cardConfigPath}`);
|
|
52
45
|
}
|
|
53
46
|
});
|
|
54
47
|
|
|
@@ -78,7 +71,7 @@ function loadConfig() {
|
|
|
78
71
|
crmCardsSubConfigFiles.forEach(card => {
|
|
79
72
|
const cardConfigPath = path.join(process.cwd(), '..', card.file);
|
|
80
73
|
try {
|
|
81
|
-
const cardConfig =
|
|
74
|
+
const cardConfig = loadConfigByPath(cardConfigPath);
|
|
82
75
|
if (!cardConfig.data) {
|
|
83
76
|
throw new Error(
|
|
84
77
|
`Invalid config file at path ${cardConfigPath}, data is a required config property`
|
|
@@ -112,7 +105,6 @@ function loadConfig() {
|
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
module.exports = {
|
|
115
|
-
getAppConfigPath,
|
|
116
108
|
loadConfigByPath,
|
|
117
109
|
loadExtensionConfig,
|
|
118
110
|
loadConfig,
|
package/cli/logger.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
const { cyan, red, yellow, magenta } = require('console-log-colors');
|
|
2
2
|
|
|
3
|
-
function info(message) {
|
|
4
|
-
console.log(cyan(message));
|
|
3
|
+
function info(message, ...data) {
|
|
4
|
+
console.log(cyan(message), ...data);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
function error(message) {
|
|
8
|
-
console.error(red(message));
|
|
7
|
+
function error(message, ...data) {
|
|
8
|
+
console.error(red(message), ...data);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function warn(message) {
|
|
12
|
-
console.info(yellow(message));
|
|
11
|
+
function warn(message, ...data) {
|
|
12
|
+
console.info(yellow(message), ...data);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function debug(message) {
|
|
16
|
-
console.debug(magenta(message));
|
|
15
|
+
function debug(message, ...data) {
|
|
16
|
+
console.debug(magenta(message), ...data);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
module.exports = { info, error, warn, debug };
|
package/cli/run.js
CHANGED
|
@@ -7,26 +7,43 @@ const {
|
|
|
7
7
|
DevModeInterface,
|
|
8
8
|
} = require('../index');
|
|
9
9
|
|
|
10
|
-
const { promptForExtensionToRun } = require('./userInput');
|
|
11
|
-
const OUTPUT_DIR = 'dist';
|
|
12
10
|
const logger = require('./logger');
|
|
13
11
|
const path = require('path');
|
|
14
|
-
const { MAIN_APP_CONFIG } = require('../lib/constants');
|
|
12
|
+
const { MAIN_APP_CONFIG, OUTPUT_DIR } = require('../lib/constants');
|
|
13
|
+
const inquirer = require('inquirer');
|
|
14
|
+
const { loadConfigByPath, loadExtensionConfig } = require('./config');
|
|
15
15
|
|
|
16
16
|
// eslint-disable-next-line no-floating-promise/no-floating-promise
|
|
17
17
|
(async () => {
|
|
18
|
-
const { DEV_MODE, BUILD_MODE, extension, help } = parseArgs();
|
|
18
|
+
const { DEV_MODE, BUILD_MODE, extension, help, alpha } = parseArgs();
|
|
19
19
|
|
|
20
20
|
if (help || !(DEV_MODE || BUILD_MODE)) {
|
|
21
21
|
showHelp(OUTPUT_DIR);
|
|
22
22
|
} else if (DEV_MODE) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const extensionPath = process.cwd(); // Assumed to be /path/to/src/app/extensions
|
|
24
|
+
const appPath = path.join(extensionPath, '..');
|
|
25
|
+
const appConfig = loadConfigByPath(path.join(appPath, MAIN_APP_CONFIG));
|
|
26
|
+
|
|
27
|
+
let extensionConfig;
|
|
28
|
+
if (extension) {
|
|
29
|
+
const allExtensionsConfig = loadExtensionConfig(appConfig, appPath);
|
|
30
|
+
extensionConfig =
|
|
31
|
+
allExtensionsConfig[path.join(extensionPath, extension)];
|
|
32
|
+
}
|
|
33
|
+
await DevModeInterface.setup({
|
|
34
|
+
alpha,
|
|
35
|
+
promptUser: inquirer.createPromptModule(),
|
|
36
|
+
components: {
|
|
37
|
+
[appConfig.name]: {
|
|
38
|
+
config: appConfig,
|
|
39
|
+
path: path.join(extensionPath, '..'),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
extensionConfig,
|
|
27
43
|
logger,
|
|
28
|
-
projectFiles: [path.join(process.cwd(), '..', MAIN_APP_CONFIG)],
|
|
29
44
|
});
|
|
45
|
+
|
|
46
|
+
await DevModeInterface.start({});
|
|
30
47
|
} else if (BUILD_MODE) {
|
|
31
48
|
if (extension) {
|
|
32
49
|
buildSingleExtension({
|
package/cli/utils.js
CHANGED
|
@@ -14,6 +14,7 @@ function parseArgs() {
|
|
|
14
14
|
const optionDefinitions = [
|
|
15
15
|
{ name: 'port', alias: 'p', type: Number },
|
|
16
16
|
{ name: 'extension', alias: 'e', type: String },
|
|
17
|
+
{ name: 'alpha', type: Boolean },
|
|
17
18
|
{ name: 'help', alias: 'h', type: Boolean },
|
|
18
19
|
];
|
|
19
20
|
|
|
@@ -48,6 +49,10 @@ function showHelp(OUTPUT_DIR) {
|
|
|
48
49
|
description:
|
|
49
50
|
'The extension entrypoint file to build or start local development for',
|
|
50
51
|
},
|
|
52
|
+
{
|
|
53
|
+
name: 'alpha',
|
|
54
|
+
description: 'Run app functions locally.',
|
|
55
|
+
},
|
|
51
56
|
{
|
|
52
57
|
name: 'help',
|
|
53
58
|
alias: 'h',
|
package/lib/DevModeInterface.js
CHANGED
|
@@ -1,66 +1,130 @@
|
|
|
1
1
|
const { startDevMode } = require('./dev');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { OUTPUT_DIR } = require('./constants');
|
|
4
|
-
const {
|
|
5
|
-
getAppConfigPath,
|
|
6
|
-
loadConfigByPath,
|
|
7
|
-
loadExtensionConfig,
|
|
8
|
-
} = require('../cli/config');
|
|
4
|
+
const { loadExtensionConfig } = require('../cli/config');
|
|
9
5
|
|
|
10
6
|
class DevModeInterface {
|
|
11
7
|
constructor() {
|
|
12
|
-
this.
|
|
8
|
+
this._setupLogger(console);
|
|
13
9
|
}
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
this.logger =
|
|
17
|
-
|
|
11
|
+
_setupLogger(_logger) {
|
|
12
|
+
this.logger = {
|
|
13
|
+
..._logger,
|
|
14
|
+
debug: (...args) => {
|
|
15
|
+
if (this.debug) {
|
|
16
|
+
_logger.debug(...args);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
22
|
+
_setDataFromExtensionConfig(extensionConfig) {
|
|
23
|
+
this.config = extensionConfig;
|
|
24
|
+
this.appName = extensionConfig.data.appName;
|
|
25
|
+
this.title = extensionConfig.data.title;
|
|
26
|
+
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const message = 'Unable to load app.json';
|
|
30
|
-
this.logger.error(message);
|
|
31
|
-
throw new Error(message);
|
|
32
|
-
}
|
|
28
|
+
_generateAppExtensionMappings(components) {
|
|
29
|
+
// Loop over all of the app configs that are passed in
|
|
30
|
+
const allComponentNames = Object.keys(components);
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
return allComponentNames.reduce((appExtensionMappings, componentName) => {
|
|
33
|
+
const component = components[componentName];
|
|
34
|
+
if (!component.config.extensions?.crm?.cards) {
|
|
35
|
+
return appExtensionMappings; // It's not an app
|
|
36
|
+
}
|
|
37
|
+
// Load all of the extension configs for a particular app.json file
|
|
38
|
+
const extensionsConfigForApp = loadExtensionConfig(
|
|
39
|
+
component.config,
|
|
40
|
+
component.path
|
|
41
|
+
);
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
const extensionFilePaths = Object.keys(extensionsConfigForApp);
|
|
44
|
+
// Loop over the loaded extension configs and generate the list of choices to use to prompt the user for input
|
|
45
|
+
extensionFilePaths.forEach(extensionPath => {
|
|
46
|
+
const extensionConfig = extensionsConfigForApp[extensionPath];
|
|
47
|
+
appExtensionMappings.push({
|
|
48
|
+
name: `${componentName}/${extensionConfig.data.title}`,
|
|
49
|
+
value: extensionConfig,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return appExtensionMappings;
|
|
54
|
+
}, []);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async setup({
|
|
58
|
+
// This flag is used for whether to enable local serverless on the dev server pre-INBOUND.
|
|
59
|
+
// If the user runs `hs project dev --local-all`, the flag is set to true.
|
|
60
|
+
// By INBOUND, we will remove the flag and make the dev server handle both extensions
|
|
61
|
+
// and app functions locally.
|
|
62
|
+
alpha = false,
|
|
63
|
+
debug = false,
|
|
64
|
+
accountId,
|
|
65
|
+
httpClient,
|
|
66
|
+
promptUser,
|
|
67
|
+
components,
|
|
68
|
+
extensionConfig,
|
|
69
|
+
logger,
|
|
70
|
+
}) {
|
|
71
|
+
this.alpha = alpha;
|
|
72
|
+
this.debug = debug;
|
|
73
|
+
this.accountId = accountId;
|
|
74
|
+
this.httpClient = httpClient;
|
|
75
|
+
if (logger) {
|
|
76
|
+
this._setupLogger(logger);
|
|
77
|
+
}
|
|
78
|
+
if (extensionConfig) {
|
|
79
|
+
this._setDataFromExtensionConfig(extensionConfig);
|
|
80
|
+
return;
|
|
41
81
|
}
|
|
42
82
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
83
|
+
const choices = this._generateAppExtensionMappings(components);
|
|
84
|
+
|
|
85
|
+
if (choices.length === 0) {
|
|
86
|
+
throw new Error('No extensions to run');
|
|
87
|
+
} else if (choices.length === 1) {
|
|
88
|
+
this._setDataFromExtensionConfig(choices[0].value);
|
|
89
|
+
} else {
|
|
90
|
+
const answers = await promptUser({
|
|
91
|
+
type: 'list',
|
|
92
|
+
name: 'extension',
|
|
93
|
+
message: 'Which extension would you like to run?',
|
|
94
|
+
choices,
|
|
95
|
+
});
|
|
96
|
+
this._setDataFromExtensionConfig(answers.extension);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
51
99
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
throw new Error(
|
|
100
|
+
async start({ debug }) {
|
|
101
|
+
this.debug = debug;
|
|
102
|
+
if (!this.config || !this.config.path) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
'Unable to load the required extension configuration files'
|
|
105
|
+
);
|
|
56
106
|
}
|
|
107
|
+
const appPath = this.config.path;
|
|
108
|
+
|
|
109
|
+
// Pass options from the CLI for running app functions locally
|
|
110
|
+
const functionsConfig = {
|
|
111
|
+
app: { path: appPath },
|
|
112
|
+
accountId: this.accountId,
|
|
113
|
+
httpClient: this.httpClient,
|
|
114
|
+
enabled: !!this.alpha,
|
|
115
|
+
};
|
|
57
116
|
|
|
58
117
|
this.shutdown = await startDevMode({
|
|
59
|
-
extensionConfig:
|
|
60
|
-
outputDir: path.join(
|
|
118
|
+
extensionConfig: this.config,
|
|
119
|
+
outputDir: path.join(this.config.extensionPath, OUTPUT_DIR),
|
|
120
|
+
functionsConfig,
|
|
61
121
|
logger: this.logger,
|
|
122
|
+
root: appPath,
|
|
62
123
|
});
|
|
63
|
-
|
|
124
|
+
|
|
125
|
+
this.logger.info(
|
|
126
|
+
`Running extension '${this.title}' from app '${this.appName}'`
|
|
127
|
+
);
|
|
64
128
|
}
|
|
65
129
|
|
|
66
130
|
async cleanup() {
|
package/lib/build.js
CHANGED
|
@@ -4,6 +4,7 @@ const manifestPlugin = require('./plugins/manifestPlugin');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { getUrlSafeFileName } = require('./utils');
|
|
6
6
|
const { loadConfig } = require('../cli/config');
|
|
7
|
+
const codeInjectionPlugin = require('./plugins/codeInjectionPlugin');
|
|
7
8
|
|
|
8
9
|
const allowedExtensions = ['.js', '.ts', '.tsx', '.jsx'];
|
|
9
10
|
const extensionErrorBaseMessage = `Supported file extensions are [${allowedExtensions.join(
|
|
@@ -20,14 +21,7 @@ async function buildAllExtensions({ outputDir, logger = console }) {
|
|
|
20
21
|
file: data.module.file,
|
|
21
22
|
outputDir,
|
|
22
23
|
emptyOutDir: i === 0,
|
|
23
|
-
|
|
24
|
-
rollup: [
|
|
25
|
-
manifestPlugin({
|
|
26
|
-
output: getUrlSafeFileName(data.module.file),
|
|
27
|
-
logger,
|
|
28
|
-
}),
|
|
29
|
-
],
|
|
30
|
-
},
|
|
24
|
+
logger,
|
|
31
25
|
});
|
|
32
26
|
}
|
|
33
27
|
}
|
|
@@ -57,7 +51,10 @@ async function buildSingleExtension({
|
|
|
57
51
|
},
|
|
58
52
|
rollupOptions: {
|
|
59
53
|
...ROLLUP_OPTIONS,
|
|
60
|
-
plugins: [
|
|
54
|
+
plugins: [
|
|
55
|
+
manifestPlugin({ output, logger }),
|
|
56
|
+
codeInjectionPlugin({ file }),
|
|
57
|
+
],
|
|
61
58
|
},
|
|
62
59
|
outDir: outputDir,
|
|
63
60
|
emptyOutDir,
|
package/lib/constants.js
CHANGED
|
@@ -18,9 +18,14 @@ const ROLLUP_OPTIONS = {
|
|
|
18
18
|
},
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const EXTENSIONS_MESSAGE_VERSION =
|
|
21
|
+
const EXTENSIONS_MESSAGE_VERSION = 1;
|
|
22
22
|
const WEBSOCKET_MESSAGE_VERSION = 0;
|
|
23
23
|
|
|
24
|
+
const SERVER_CAPABILITIES = [
|
|
25
|
+
// Supports running app functions locally
|
|
26
|
+
'app-functions-local-dev',
|
|
27
|
+
];
|
|
28
|
+
|
|
24
29
|
module.exports = {
|
|
25
30
|
ROLLUP_OPTIONS,
|
|
26
31
|
OUTPUT_DIR,
|
|
@@ -30,4 +35,5 @@ module.exports = {
|
|
|
30
35
|
VITE_DEFAULT_PORT,
|
|
31
36
|
WEBSOCKET_MESSAGE_VERSION,
|
|
32
37
|
WEBSOCKET_PORT,
|
|
38
|
+
SERVER_CAPABILITIES,
|
|
33
39
|
};
|
package/lib/dev.js
CHANGED
|
@@ -14,9 +14,11 @@ async function _createViteDevServer(
|
|
|
14
14
|
extensionConfig,
|
|
15
15
|
websocketPort,
|
|
16
16
|
baseMessage,
|
|
17
|
-
logger
|
|
17
|
+
logger,
|
|
18
|
+
root
|
|
18
19
|
) {
|
|
19
20
|
return await createServer({
|
|
21
|
+
root,
|
|
20
22
|
appType: 'custom',
|
|
21
23
|
mode: 'development',
|
|
22
24
|
server: {
|
|
@@ -48,10 +50,12 @@ async function _createViteDevServer(
|
|
|
48
50
|
|
|
49
51
|
async function startDevMode({
|
|
50
52
|
extensionConfig,
|
|
53
|
+
functionsConfig,
|
|
51
54
|
logger,
|
|
52
55
|
outputDir = OUTPUT_DIR,
|
|
53
56
|
expressPort = VITE_DEFAULT_PORT,
|
|
54
57
|
webSocketPort = WEBSOCKET_PORT,
|
|
58
|
+
root = process.cwd(),
|
|
55
59
|
}) {
|
|
56
60
|
if (!extensionConfig) {
|
|
57
61
|
throw new Error('Unable to determine which extension to run');
|
|
@@ -68,16 +72,19 @@ async function startDevMode({
|
|
|
68
72
|
extensionConfig,
|
|
69
73
|
webSocketPort,
|
|
70
74
|
baseMessage,
|
|
71
|
-
logger
|
|
75
|
+
logger,
|
|
76
|
+
root
|
|
72
77
|
);
|
|
73
|
-
|
|
78
|
+
|
|
79
|
+
return startDevServer({
|
|
74
80
|
outputDir,
|
|
75
81
|
expressPort,
|
|
76
82
|
webSocketPort,
|
|
77
83
|
baseMessage,
|
|
78
84
|
viteDevServer,
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
functionsConfig,
|
|
86
|
+
logger,
|
|
87
|
+
});
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
module.exports = {
|
package/lib/extensionsService.js
CHANGED
|
@@ -7,15 +7,25 @@ class ExtensionsService {
|
|
|
7
7
|
this.endpoint = '/extensions';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
add(server, webSocketPort, outputDir, baseMessage) {
|
|
10
|
+
add(server, webSocketPort, outputDir, baseMessage, capabilities) {
|
|
11
11
|
server.get(
|
|
12
12
|
this.endpoint,
|
|
13
|
-
this.generateExtensionsHandler(
|
|
13
|
+
this.generateExtensionsHandler(
|
|
14
|
+
baseMessage,
|
|
15
|
+
webSocketPort,
|
|
16
|
+
outputDir,
|
|
17
|
+
capabilities
|
|
18
|
+
)
|
|
14
19
|
);
|
|
15
20
|
return [this.endpoint];
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
generateExtensionsHandler(
|
|
23
|
+
generateExtensionsHandler(
|
|
24
|
+
baseMessage,
|
|
25
|
+
webSocketPort,
|
|
26
|
+
outputDir,
|
|
27
|
+
capabilities = []
|
|
28
|
+
) {
|
|
19
29
|
return function extensionsHandler(_req, res) {
|
|
20
30
|
try {
|
|
21
31
|
const output = path.parse(baseMessage.callback).name;
|
|
@@ -23,6 +33,7 @@ class ExtensionsService {
|
|
|
23
33
|
const response = {
|
|
24
34
|
websocket: `ws://localhost:${webSocketPort}`,
|
|
25
35
|
version: EXTENSIONS_MESSAGE_VERSION,
|
|
36
|
+
capabilities,
|
|
26
37
|
extensions: [
|
|
27
38
|
{
|
|
28
39
|
...baseMessage,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
function codeInjectionPlugin(options = {}) {
|
|
4
|
+
const { file, root = process.cwd() } = options;
|
|
5
|
+
return {
|
|
6
|
+
name: 'ui-extensions-code-injection-plugin',
|
|
7
|
+
enforce: 'post', // run after default rollup plugins
|
|
8
|
+
transform(code, fileBeingTransformed) {
|
|
9
|
+
const absoluteFilePath = path.isAbsolute(file)
|
|
10
|
+
? file
|
|
11
|
+
: path.join(root, file);
|
|
12
|
+
|
|
13
|
+
if (fileBeingTransformed !== absoluteFilePath) {
|
|
14
|
+
return { code, map: null }; // Not the file we care about, return the same code
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Update the code to import the self script which houses our overrides
|
|
18
|
+
// This needs to be the first line in the source file so that the overrides get hoisted
|
|
19
|
+
// to the top of the generated source file
|
|
20
|
+
const updatedCode = `import "@hubspot/ui-extensions-dev-server/self"; ${code}`;
|
|
21
|
+
|
|
22
|
+
// Return the updated source code. We don't need to include the new code in the
|
|
23
|
+
// sourcemap because we don't want it to show up in the users code when they
|
|
24
|
+
// view the source mapped code in the browser
|
|
25
|
+
return { code: updatedCode, map: null };
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = codeInjectionPlugin;
|
|
@@ -2,6 +2,7 @@ const { ROLLUP_OPTIONS, WEBSOCKET_MESSAGE_VERSION } = require('../constants');
|
|
|
2
2
|
const { build } = require('vite');
|
|
3
3
|
const manifestPlugin = require('./manifestPlugin');
|
|
4
4
|
const { stripAnsiColorCodes } = require('../utils');
|
|
5
|
+
const codeInjectionPlugin = require('./codeInjectionPlugin');
|
|
5
6
|
|
|
6
7
|
function devBuildPlugin(options = {}) {
|
|
7
8
|
const { extensionConfig, outputDir, baseMessage, logger } = options;
|
|
@@ -36,6 +37,7 @@ function devBuildPlugin(options = {}) {
|
|
|
36
37
|
const devBuild = async server => {
|
|
37
38
|
try {
|
|
38
39
|
await build({
|
|
40
|
+
logLevel: 'warn',
|
|
39
41
|
mode: 'development',
|
|
40
42
|
define: {
|
|
41
43
|
'process.env.NODE_ENV': JSON.stringify(
|
|
@@ -57,6 +59,7 @@ function devBuildPlugin(options = {}) {
|
|
|
57
59
|
output: extensionConfig.output,
|
|
58
60
|
logger,
|
|
59
61
|
}),
|
|
62
|
+
codeInjectionPlugin({ file: extensionConfig.data.module.file }),
|
|
60
63
|
],
|
|
61
64
|
output: {
|
|
62
65
|
...ROLLUP_OPTIONS.output,
|
|
@@ -71,6 +74,7 @@ function devBuildPlugin(options = {}) {
|
|
|
71
74
|
});
|
|
72
75
|
return true;
|
|
73
76
|
} catch (error) {
|
|
77
|
+
logger.error(error);
|
|
74
78
|
handleBuildError(error, server);
|
|
75
79
|
return false;
|
|
76
80
|
}
|
|
@@ -111,7 +115,7 @@ function devBuildPlugin(options = {}) {
|
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
if (server.ws.clients.size === 0) {
|
|
114
|
-
logger.
|
|
118
|
+
logger.warn('Bundle updated, no browsers connected to notify');
|
|
115
119
|
return [];
|
|
116
120
|
}
|
|
117
121
|
|
package/lib/self.js
ADDED
package/lib/server.js
CHANGED
|
@@ -1,36 +1,53 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const cors = require('cors');
|
|
3
|
+
const { SERVER_CAPABILITIES } = require('./constants');
|
|
3
4
|
const extensionsService = require('./extensionsService');
|
|
5
|
+
const {
|
|
6
|
+
AppFunctionExecutionService,
|
|
7
|
+
} = require('@hubspot/app-functions-dev-server');
|
|
4
8
|
|
|
5
|
-
function startDevServer(
|
|
9
|
+
function startDevServer({
|
|
6
10
|
outputDir,
|
|
7
11
|
expressPort,
|
|
8
12
|
webSocketPort,
|
|
9
13
|
baseMessage,
|
|
10
14
|
viteDevServer,
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
functionsConfig,
|
|
16
|
+
logger,
|
|
17
|
+
}) {
|
|
13
18
|
const app = express();
|
|
14
19
|
|
|
15
20
|
// Setup middleware
|
|
16
21
|
app.use(cors());
|
|
17
22
|
app.use(express.static(outputDir));
|
|
23
|
+
|
|
24
|
+
const capabilities = functionsConfig.enabled ? SERVER_CAPABILITIES : [];
|
|
25
|
+
|
|
26
|
+
if (functionsConfig.enabled) {
|
|
27
|
+
app.use(
|
|
28
|
+
'/api/crm-extensibility/execution/internal/v3',
|
|
29
|
+
AppFunctionExecutionService({ ...functionsConfig, logger })
|
|
30
|
+
);
|
|
31
|
+
logger.info('Serving app functions locally');
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
const endpointsAdded = extensionsService.add(
|
|
19
35
|
app,
|
|
20
36
|
webSocketPort,
|
|
21
37
|
outputDir,
|
|
22
|
-
baseMessage
|
|
38
|
+
baseMessage,
|
|
39
|
+
capabilities
|
|
23
40
|
);
|
|
24
41
|
|
|
25
42
|
endpointsAdded.forEach(endpoint => {
|
|
26
|
-
logger.
|
|
43
|
+
logger.debug(`Listening at http://hslocal.net:${expressPort}${endpoint}`);
|
|
27
44
|
});
|
|
28
45
|
|
|
29
46
|
// Vite middlewares needs to go last because it's greedy and will block other middleware
|
|
30
47
|
app.use(viteDevServer.middlewares);
|
|
31
48
|
|
|
32
49
|
const server = app.listen({ port: expressPort }, () => {
|
|
33
|
-
logger.
|
|
50
|
+
logger.debug(`Listening at ${baseMessage.callback}`);
|
|
34
51
|
});
|
|
35
52
|
|
|
36
53
|
return async function shutdown() {
|
|
@@ -38,7 +55,6 @@ function startDevServer(
|
|
|
38
55
|
// Stop new connections to express server
|
|
39
56
|
server.close(() => {});
|
|
40
57
|
logger.info('Clean up done, exiting.');
|
|
41
|
-
process.exit(0);
|
|
42
58
|
};
|
|
43
59
|
}
|
|
44
60
|
|
package/lib/utils.js
CHANGED
|
@@ -8,8 +8,11 @@ function getUrlSafeFileName(filePath) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
// Strips ANSI color codes out of strings because we don't want to pass them to the browser
|
|
11
|
-
function stripAnsiColorCodes(
|
|
12
|
-
|
|
11
|
+
function stripAnsiColorCodes(stringWithColorCodes) {
|
|
12
|
+
if (!stringWithColorCodes) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return stringWithColorCodes.replace(
|
|
13
16
|
// eslint-disable-next-line no-control-regex
|
|
14
17
|
/[\u001b][[]*([0-9]{1,4};?)*[m]/g,
|
|
15
18
|
''
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/ui-extensions-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./index.js",
|
|
15
|
+
"./self": "./lib/self.js"
|
|
16
|
+
},
|
|
13
17
|
"files": [
|
|
14
18
|
"cli/config.js",
|
|
15
|
-
"cli/extensions.js",
|
|
16
19
|
"cli/logger.js",
|
|
17
20
|
"cli/run.js",
|
|
18
|
-
"cli/userInput.js",
|
|
19
21
|
"cli/utils.js",
|
|
20
22
|
"lib/plugins/*",
|
|
21
23
|
"lib/build.js",
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
"lib/dev.js",
|
|
24
26
|
"lib/extensionsService.js",
|
|
25
27
|
"lib/DevModeInterface.js",
|
|
28
|
+
"lib/self.js",
|
|
26
29
|
"lib/server.js",
|
|
27
30
|
"lib/utils.js",
|
|
28
31
|
"index.js",
|
|
@@ -30,12 +33,13 @@
|
|
|
30
33
|
],
|
|
31
34
|
"license": "MIT",
|
|
32
35
|
"dependencies": {
|
|
36
|
+
"@hubspot/app-functions-dev-server": "^0.6.0",
|
|
33
37
|
"command-line-args": "^5.2.1",
|
|
34
38
|
"command-line-usage": "^7.0.1",
|
|
35
39
|
"console-log-colors": "^0.4.0",
|
|
36
40
|
"cors": "^2.8.5",
|
|
37
41
|
"express": "^4.18.2",
|
|
38
|
-
"
|
|
42
|
+
"inquirer": "8.2.0",
|
|
39
43
|
"vite": "^4.0.4"
|
|
40
44
|
},
|
|
41
45
|
"devDependencies": {
|
|
@@ -63,5 +67,5 @@
|
|
|
63
67
|
"optional": true
|
|
64
68
|
}
|
|
65
69
|
},
|
|
66
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "1979c2dce0a920c10e4f8b5ec3bc16a1080120cb"
|
|
67
71
|
}
|
package/cli/extensions.js
DELETED
package/cli/userInput.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const prompts = require('prompts');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const { getExtensionsList } = require('./extensions');
|
|
5
|
-
|
|
6
|
-
async function promptForExtensionToRun() {
|
|
7
|
-
const extensionOptions = getExtensionsList();
|
|
8
|
-
const response = await prompts(
|
|
9
|
-
[
|
|
10
|
-
{
|
|
11
|
-
type: 'select',
|
|
12
|
-
name: 'extension',
|
|
13
|
-
message: 'Which extension would you like to run?',
|
|
14
|
-
choices: extensionOptions.map(option => {
|
|
15
|
-
return {
|
|
16
|
-
title: option,
|
|
17
|
-
value: option,
|
|
18
|
-
};
|
|
19
|
-
}),
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
|
-
{
|
|
23
|
-
onCancel: () => {
|
|
24
|
-
process.exit(0); // When the user cancels interaction, exit the script
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
return path.parse(response.extension).name;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
promptForExtensionToRun,
|
|
33
|
-
};
|