@hubspot/ui-extensions-dev-server 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,51 +1,53 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions-dev-server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo 'test'"
7
+ "test": "jest",
8
+ "jest": "jest --watch"
8
9
  },
9
10
  "publishConfig": {
10
11
  "access": "public"
11
12
  },
12
13
  "files": [
13
- "README.md",
14
- "build.js",
15
- "cli.js",
16
- "config.js",
17
- "constants.js",
18
- "dev.js",
19
- "logger.js",
20
- "run.js",
21
- "utils.js",
22
- "tests/runTests.js",
23
- "tests/testBuild.js",
24
- "tests/testDevServer.js",
25
- "tests/utils.js",
26
- "plugins/*",
14
+ "cli/logger.js",
15
+ "cli/run.js",
16
+ "cli/userInput.js",
17
+ "cli/utils.js",
18
+ "lib/plugins/*",
19
+ "lib/build.js",
20
+ "lib/config.js",
21
+ "lib/constants.js",
22
+ "lib/dev.js",
23
+ "lib/extensions.js",
24
+ "lib/server.js",
25
+ "lib/utils.js",
27
26
  "index.js",
28
- "server.js"
27
+ "README.md"
29
28
  ],
30
29
  "license": "MIT",
31
30
  "dependencies": {
32
- "axios": "^1.4.0",
33
31
  "command-line-args": "^5.2.1",
34
32
  "command-line-usage": "^7.0.1",
35
33
  "console-log-colors": "^0.4.0",
36
34
  "cors": "^2.8.5",
37
35
  "express": "^4.18.2",
38
- "process": "^0.11.10",
39
36
  "prompts": "^2.4.2",
40
- "vite": "^4.0.4",
37
+ "vite": "^4.0.4"
38
+ },
39
+ "devDependencies": {
40
+ "axios": "^1.4.0",
41
+ "jest": "^29.5.0",
41
42
  "ws": "^8.13.0"
42
43
  },
43
44
  "bin": {
44
- "hs-ui-extensions-dev-server": "run.js"
45
+ "hs-ui-extensions-dev-server": "./cli/run.js"
45
46
  },
46
47
  "eslintConfig": {
47
48
  "env": {
48
- "node": true
49
+ "node": true,
50
+ "jest": true
49
51
  }
50
52
  },
51
53
  "engines": {
@@ -59,5 +61,5 @@
59
61
  "optional": true
60
62
  }
61
63
  },
62
- "gitHead": "ab7ad192e9d98163d2613d77f68677e3b48898aa"
64
+ "gitHead": "0b20c005a9564bbde58c22ce21959c713f354607"
63
65
  }
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
- })();
@@ -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
- };