@hubspot/ui-extensions-dev-server 0.2.0 → 0.3.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.
- package/cli/run.js +36 -0
- package/cli/userInput.js +32 -0
- package/index.js +9 -29
- package/lib/build.js +90 -0
- package/lib/config.js +70 -0
- package/{constants.js → lib/constants.js} +7 -8
- package/lib/dev.js +89 -0
- package/lib/extensions.js +9 -0
- package/{plugins → lib/plugins}/devBuildPlugin.js +55 -14
- package/{plugins → lib/plugins}/manifestPlugin.js +1 -2
- package/{server.js → lib/server.js} +8 -6
- package/lib/utils.js +20 -0
- package/package.json +25 -23
- package/build.js +0 -60
- package/config.js +0 -129
- package/dev.js +0 -64
- package/run.js +0 -41
- package/tests/runTests.js +0 -50
- package/tests/testBuild.js +0 -82
- package/tests/testDevServer.js +0 -287
- package/tests/utils.js +0 -76
- package/utils.js +0 -10
- /package/{logger.js → cli/logger.js} +0 -0
- /package/{cli.js → cli/utils.js} +0 -0
package/build.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const { build } = require('vite');
|
|
2
|
-
const { ROLLUP_OPTIONS } = require('./constants');
|
|
3
|
-
const manifestPlugin = require('./plugins/manifestPlugin');
|
|
4
|
-
|
|
5
|
-
async function buildAllExtensions(config, outputDir) {
|
|
6
|
-
const extensionKeys = Object.keys(config);
|
|
7
|
-
for (let i = 0; i < extensionKeys.length; ++i) {
|
|
8
|
-
const { data } = config[extensionKeys[i]];
|
|
9
|
-
|
|
10
|
-
await buildSingleExtension({
|
|
11
|
-
file: data?.module.file,
|
|
12
|
-
outputFileName: data?.output,
|
|
13
|
-
outputDir,
|
|
14
|
-
emptyOutDir: i === 0,
|
|
15
|
-
plugins: {
|
|
16
|
-
rollup: [manifestPlugin({ output: data?.output })],
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function buildSingleExtension({
|
|
23
|
-
file,
|
|
24
|
-
outputFileName,
|
|
25
|
-
outputDir,
|
|
26
|
-
emptyOutDir = true,
|
|
27
|
-
plugins = { rollup: [], vite: [] },
|
|
28
|
-
minify = false,
|
|
29
|
-
root = process.cwd(), // This is the vite default, so using that as our default
|
|
30
|
-
}) {
|
|
31
|
-
await build({
|
|
32
|
-
root,
|
|
33
|
-
define: {
|
|
34
|
-
'process.env.NODE_ENV': JSON.stringify(
|
|
35
|
-
process.env.NODE_ENV || 'production'
|
|
36
|
-
),
|
|
37
|
-
},
|
|
38
|
-
build: {
|
|
39
|
-
lib: {
|
|
40
|
-
entry: file,
|
|
41
|
-
name: outputFileName,
|
|
42
|
-
formats: ['iife'],
|
|
43
|
-
fileName: () => outputFileName,
|
|
44
|
-
},
|
|
45
|
-
rollupOptions: {
|
|
46
|
-
...ROLLUP_OPTIONS,
|
|
47
|
-
plugins: [...(ROLLUP_OPTIONS.plugins || []), ...plugins?.rollup],
|
|
48
|
-
},
|
|
49
|
-
outDir: outputDir,
|
|
50
|
-
emptyOutDir,
|
|
51
|
-
minify,
|
|
52
|
-
plugins: plugins?.vite,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
module.exports = {
|
|
58
|
-
buildAllExtensions,
|
|
59
|
-
buildSingleExtension,
|
|
60
|
-
};
|
package/config.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
const prompts = require('prompts');
|
|
2
|
-
const logger = require('./logger');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const { MAIN_APP_CONFIG } = require('./constants');
|
|
5
|
-
const { getUrlSafeFileName } = require('./utils');
|
|
6
|
-
|
|
7
|
-
async function getExtensionConfig(configuration, extension) {
|
|
8
|
-
const extensionOptions = Object.keys(configuration);
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
(extension && configuration[extension]) ||
|
|
12
|
-
extensionOptions.length === 1
|
|
13
|
-
) {
|
|
14
|
-
const extensionToRun = extension || extensionOptions[0];
|
|
15
|
-
const { data } = configuration[extensionToRun];
|
|
16
|
-
return {
|
|
17
|
-
key: extensionToRun,
|
|
18
|
-
name: data.title,
|
|
19
|
-
file: data?.module?.file,
|
|
20
|
-
output: data?.output,
|
|
21
|
-
appName: data?.appName,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const response = await prompts(
|
|
26
|
-
[
|
|
27
|
-
{
|
|
28
|
-
type: 'select',
|
|
29
|
-
name: 'extension',
|
|
30
|
-
message: 'Which extension would you like to run?',
|
|
31
|
-
choices: extensionOptions.map(option => {
|
|
32
|
-
const { data } = configuration[option];
|
|
33
|
-
return {
|
|
34
|
-
title: option,
|
|
35
|
-
value: {
|
|
36
|
-
key: option,
|
|
37
|
-
name: data?.title,
|
|
38
|
-
file: data?.module?.file,
|
|
39
|
-
output: data?.output,
|
|
40
|
-
appName: data?.appName,
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
}),
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
{
|
|
47
|
-
onCancel: () => {
|
|
48
|
-
process.exit(0); // When the user cancels interaction, exit the script
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
return response.extension;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function _loadRequiredConfigFile(filePath) {
|
|
56
|
-
let config;
|
|
57
|
-
try {
|
|
58
|
-
config = require(filePath);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
logger.error(
|
|
61
|
-
`Unable to load ${filePath} file. Please make sure you are running the command from the src/app/extensions directory and that ${filePath} exists`
|
|
62
|
-
);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
return config;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function loadConfig() {
|
|
69
|
-
// app.json is one level up from the extensions directory, which is where these commands
|
|
70
|
-
// will need to be ran from, the extensions directory
|
|
71
|
-
const configPath = path.join(process.cwd(), `../${MAIN_APP_CONFIG}`);
|
|
72
|
-
|
|
73
|
-
const mainAppConfig = _loadRequiredConfigFile(configPath);
|
|
74
|
-
|
|
75
|
-
const crmCardsSubConfigFiles = mainAppConfig?.extensions?.crm?.cards;
|
|
76
|
-
if (!crmCardsSubConfigFiles) {
|
|
77
|
-
logger.error(
|
|
78
|
-
`The "extensions.crm.cards" array in ${configPath} is missing, it is a required configuration property`
|
|
79
|
-
);
|
|
80
|
-
process.exit(1);
|
|
81
|
-
} else if (crmCardsSubConfigFiles.length === 0) {
|
|
82
|
-
logger.error(
|
|
83
|
-
`The "extensions.crm.cards" array in ${configPath} is empty, it is a required configuration property.`
|
|
84
|
-
);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const outputConfig = {};
|
|
89
|
-
|
|
90
|
-
crmCardsSubConfigFiles.forEach(card => {
|
|
91
|
-
const extensionsRemoved = card.file.replace('extensions/', '');
|
|
92
|
-
const cardConfigPath = path.join(process.cwd(), extensionsRemoved);
|
|
93
|
-
// Get the path to the config file relative to the extensions directory
|
|
94
|
-
const configPathRelativeToExtensions = path.parse(extensionsRemoved)?.dir;
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const cardConfig = require(cardConfigPath);
|
|
98
|
-
|
|
99
|
-
// Join the two relative paths
|
|
100
|
-
const entryPointPath = path.join(
|
|
101
|
-
configPathRelativeToExtensions,
|
|
102
|
-
cardConfig.data?.module?.file
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
cardConfig.data.module.file = entryPointPath;
|
|
106
|
-
|
|
107
|
-
outputConfig[entryPointPath] = cardConfig;
|
|
108
|
-
outputConfig[entryPointPath].data.output = getUrlSafeFileName(
|
|
109
|
-
entryPointPath
|
|
110
|
-
);
|
|
111
|
-
outputConfig[entryPointPath].data.appName = mainAppConfig.name;
|
|
112
|
-
} catch (e) {
|
|
113
|
-
let errorMessage = e?.message;
|
|
114
|
-
if (e?.code === 'MODULE_NOT_FOUND') {
|
|
115
|
-
errorMessage = `Unable to load "${cardConfigPath}" file. \nPlease make sure you are running the command from the src/app/extensions directory and that your card JSON config exists within it.`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
logger.error(errorMessage);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return outputConfig;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
module.exports = {
|
|
127
|
-
loadConfig,
|
|
128
|
-
getExtensionConfig,
|
|
129
|
-
};
|
package/dev.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
const { createServer } = require('vite');
|
|
2
|
-
const { getExtensionConfig } = require('./config');
|
|
3
|
-
const startDevServer = require('./server');
|
|
4
|
-
const devBuildPlugin = require('./plugins/devBuildPlugin');
|
|
5
|
-
|
|
6
|
-
async function _createViteDevServer(
|
|
7
|
-
outputDir,
|
|
8
|
-
extensionConfig,
|
|
9
|
-
websocketPort,
|
|
10
|
-
baseMessage
|
|
11
|
-
) {
|
|
12
|
-
return await createServer({
|
|
13
|
-
appType: 'custom',
|
|
14
|
-
mode: 'development',
|
|
15
|
-
server: {
|
|
16
|
-
middlewareMode: true,
|
|
17
|
-
hmr: {
|
|
18
|
-
port: websocketPort,
|
|
19
|
-
},
|
|
20
|
-
watch: {
|
|
21
|
-
ignored: ['**/src/app/extensions/dist/**/*'],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
build: {
|
|
25
|
-
rollupOptions: {
|
|
26
|
-
input: extensionConfig?.file,
|
|
27
|
-
output: extensionConfig.output,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
plugins: [devBuildPlugin({ extensionConfig, outputDir, baseMessage })],
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function startDevMode(
|
|
35
|
-
config,
|
|
36
|
-
outputDir,
|
|
37
|
-
expressPort,
|
|
38
|
-
webSocketPort,
|
|
39
|
-
extension
|
|
40
|
-
) {
|
|
41
|
-
const extensionConfig = await getExtensionConfig(config, extension);
|
|
42
|
-
const baseMessage = Object.freeze({
|
|
43
|
-
appName: extensionConfig?.appName,
|
|
44
|
-
title: extensionConfig?.name,
|
|
45
|
-
callback: `http://hslocal.net:${expressPort}/${extensionConfig?.output}`,
|
|
46
|
-
});
|
|
47
|
-
const viteDevServer = await _createViteDevServer(
|
|
48
|
-
outputDir,
|
|
49
|
-
extensionConfig,
|
|
50
|
-
webSocketPort,
|
|
51
|
-
baseMessage
|
|
52
|
-
);
|
|
53
|
-
startDevServer(
|
|
54
|
-
outputDir,
|
|
55
|
-
expressPort,
|
|
56
|
-
webSocketPort,
|
|
57
|
-
baseMessage,
|
|
58
|
-
viteDevServer
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = {
|
|
63
|
-
startDevMode,
|
|
64
|
-
};
|
package/run.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { startDevMode } = require('./dev');
|
|
4
|
-
const { loadConfig } = require('./config');
|
|
5
|
-
const { buildAllExtensions, buildSingleExtension } = require('./build');
|
|
6
|
-
const {
|
|
7
|
-
VITE_DEFAULT_PORT,
|
|
8
|
-
OUTPUT_DIR,
|
|
9
|
-
WEBSOCKET_PORT,
|
|
10
|
-
} = require('./constants');
|
|
11
|
-
const { parseArgs, showHelp } = require('./cli');
|
|
12
|
-
const { getUrlSafeFileName } = require('./utils');
|
|
13
|
-
const manifestPlugin = require('./plugins/manifestPlugin');
|
|
14
|
-
|
|
15
|
-
const { DEV_MODE, BUILD_MODE, extension, help } = parseArgs();
|
|
16
|
-
|
|
17
|
-
if (help || !(DEV_MODE || BUILD_MODE)) {
|
|
18
|
-
showHelp(OUTPUT_DIR);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (DEV_MODE) {
|
|
22
|
-
startDevMode(
|
|
23
|
-
loadConfig(),
|
|
24
|
-
OUTPUT_DIR,
|
|
25
|
-
VITE_DEFAULT_PORT,
|
|
26
|
-
WEBSOCKET_PORT,
|
|
27
|
-
extension
|
|
28
|
-
);
|
|
29
|
-
} else if (BUILD_MODE) {
|
|
30
|
-
if (extension) {
|
|
31
|
-
const output = getUrlSafeFileName(extension);
|
|
32
|
-
buildSingleExtension({
|
|
33
|
-
file: extension,
|
|
34
|
-
outputFileName: output,
|
|
35
|
-
outputDir: OUTPUT_DIR,
|
|
36
|
-
plugins: { rollup: [manifestPlugin({ output })] },
|
|
37
|
-
});
|
|
38
|
-
} else {
|
|
39
|
-
buildAllExtensions(loadConfig(), OUTPUT_DIR);
|
|
40
|
-
}
|
|
41
|
-
}
|
package/tests/runTests.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { testBuild } = require('./testBuild');
|
|
4
|
-
const { testDevServer } = require('./testDevServer');
|
|
5
|
-
const logger = require('../logger');
|
|
6
|
-
const { generateSpec } = require('./utils');
|
|
7
|
-
|
|
8
|
-
let devServerProcess;
|
|
9
|
-
|
|
10
|
-
// Overwrite console.debug to only log when in DEBUG mode
|
|
11
|
-
console.debug = (...args) => {
|
|
12
|
-
if (process.env.DEBUG) {
|
|
13
|
-
console.log(...args);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function handleFailure() {
|
|
18
|
-
if (devServerProcess) {
|
|
19
|
-
devServerProcess.kill();
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
process.on('SIGINT', handleFailure);
|
|
24
|
-
process.on('SIGTERM', handleFailure);
|
|
25
|
-
|
|
26
|
-
process.on('uncaughtException', e => {
|
|
27
|
-
console.error(e);
|
|
28
|
-
handleFailure();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// eslint-disable-next-line no-floating-promise/no-floating-promise
|
|
32
|
-
(async () => {
|
|
33
|
-
try {
|
|
34
|
-
const spec = generateSpec();
|
|
35
|
-
|
|
36
|
-
logger.warn(
|
|
37
|
-
`Running tests for the following entrypoints: [\n\t${spec.build.entrypoints.join(
|
|
38
|
-
',\n\t'
|
|
39
|
-
)}\n]`
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
await testBuild(logger, spec.build);
|
|
43
|
-
testDevServer(logger, devServerProcess, spec.dev);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
console.error(e.message);
|
|
46
|
-
logger.error('Tests failed 😭');
|
|
47
|
-
handleFailure();
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
})();
|
package/tests/testBuild.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const assert = require('assert');
|
|
5
|
-
const { generateManifestOutputPair, verifyFileContents } = require('./utils');
|
|
6
|
-
|
|
7
|
-
function _testHelper(command, outputDirFiles) {
|
|
8
|
-
if (command) {
|
|
9
|
-
execSync(command);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Make sure the files are getting generated in the dist dir
|
|
13
|
-
const distDir = path.join(process.cwd(), 'dist');
|
|
14
|
-
const filesInOutputDir = fs.readdirSync(distDir);
|
|
15
|
-
assert.deepStrictEqual(filesInOutputDir, outputDirFiles);
|
|
16
|
-
|
|
17
|
-
// Spot check the file contents to make sure they seem ok
|
|
18
|
-
filesInOutputDir.forEach(file => {
|
|
19
|
-
const fileContents = fs.readFileSync(path.join(distDir, file)).toString();
|
|
20
|
-
verifyFileContents(file, fileContents);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function testBuildAll(logger, entrypoints) {
|
|
25
|
-
const expected = entrypoints
|
|
26
|
-
.reduce((acc, cur) => {
|
|
27
|
-
return acc.concat(generateManifestOutputPair(cur));
|
|
28
|
-
}, [])
|
|
29
|
-
.sort();
|
|
30
|
-
logger.warn('- Test build all extensions started 🤞');
|
|
31
|
-
_testHelper('hs-ui-extensions-dev-server build', expected);
|
|
32
|
-
logger.info('- Test build all extensions passed 🚀');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function testBuildSingle(logger, entrypoints) {
|
|
36
|
-
entrypoints.forEach(entrypoint => {
|
|
37
|
-
logger.warn(
|
|
38
|
-
`- Test build single extension started for entrypoint ${entrypoint} 🤞`
|
|
39
|
-
);
|
|
40
|
-
_testHelper(
|
|
41
|
-
`hs-ui-extensions-dev-server build --extension ${entrypoint}`,
|
|
42
|
-
generateManifestOutputPair(entrypoint)
|
|
43
|
-
);
|
|
44
|
-
logger.info(
|
|
45
|
-
`- Test build single extension passed for entrypoint ${entrypoint} 🚀`
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function testBuildRemote(logger, entrypoints) {
|
|
51
|
-
const { remoteBuild } = require('../index');
|
|
52
|
-
for (let i = 0; i < entrypoints.length; ++i) {
|
|
53
|
-
const entrypoint = entrypoints[i];
|
|
54
|
-
logger.warn(
|
|
55
|
-
`- Test remoteBuild function started for entrypoint ${entrypoint} 🤞`
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
await remoteBuild(process.cwd(), entrypoint, 'dist');
|
|
59
|
-
_testHelper(null, generateManifestOutputPair(entrypoint));
|
|
60
|
-
|
|
61
|
-
logger.info(
|
|
62
|
-
`- Test remoteBuild function passed for entrypoint ${entrypoint} 🚀`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function testBuild(logger, buildSpec) {
|
|
68
|
-
const { entrypoints } = buildSpec;
|
|
69
|
-
|
|
70
|
-
logger.warn('\nBuild Tests started - External Devs 🤞');
|
|
71
|
-
testBuildAll(logger, entrypoints);
|
|
72
|
-
testBuildSingle(logger, entrypoints);
|
|
73
|
-
logger.info('Build Tests passed - External Devs🚀');
|
|
74
|
-
|
|
75
|
-
logger.warn('\nBuild Tests started - Remote 🤞');
|
|
76
|
-
await testBuildRemote(logger, entrypoints);
|
|
77
|
-
logger.info('Build Tests passed - Remote 🚀');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
module.exports = {
|
|
81
|
-
testBuild,
|
|
82
|
-
};
|