@hubspot/ui-extensions-dev-server 0.7.3 → 0.8.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.
Files changed (52) hide show
  1. package/dist/cli/config.d.ts +8 -0
  2. package/dist/cli/config.js +80 -0
  3. package/dist/cli/run.d.ts +2 -0
  4. package/dist/cli/run.js +89 -0
  5. package/dist/cli/utils.d.ts +3 -0
  6. package/dist/cli/utils.js +79 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.js +12 -0
  9. package/dist/lib/DevModeInterface.d.ts +35 -0
  10. package/dist/lib/DevModeInterface.js +121 -0
  11. package/dist/lib/build.d.ts +14 -0
  12. package/dist/lib/build.js +82 -0
  13. package/dist/lib/constants.d.ts +18 -0
  14. package/dist/lib/constants.js +28 -0
  15. package/dist/lib/dev.d.ts +13 -0
  16. package/dist/lib/dev.js +112 -0
  17. package/dist/lib/extensionsService.d.ts +12 -0
  18. package/dist/lib/extensionsService.js +40 -0
  19. package/dist/lib/plugins/codeCheckingPlugin.d.ts +7 -0
  20. package/dist/lib/plugins/codeCheckingPlugin.js +27 -0
  21. package/dist/lib/plugins/codeInjectionPlugin.d.ts +8 -0
  22. package/dist/lib/plugins/codeInjectionPlugin.js +30 -0
  23. package/dist/lib/plugins/devBuildPlugin.d.ts +11 -0
  24. package/dist/lib/plugins/devBuildPlugin.js +140 -0
  25. package/dist/lib/plugins/friendlyLoggingPlugin.d.ts +11 -0
  26. package/dist/lib/plugins/friendlyLoggingPlugin.js +44 -0
  27. package/dist/lib/plugins/manifestPlugin.d.ts +9 -0
  28. package/dist/lib/plugins/manifestPlugin.js +96 -0
  29. package/dist/lib/self.d.ts +0 -0
  30. package/{lib → dist/lib}/self.js +1 -1
  31. package/dist/lib/server.d.ts +13 -0
  32. package/dist/lib/server.js +68 -0
  33. package/dist/lib/types.d.ts +53 -0
  34. package/dist/lib/types.js +2 -0
  35. package/dist/lib/utils.d.ts +5 -0
  36. package/dist/lib/utils.js +42 -0
  37. package/package.json +20 -23
  38. package/cli/config.js +0 -111
  39. package/cli/run.js +0 -62
  40. package/cli/utils.js +0 -67
  41. package/index.js +0 -14
  42. package/lib/DevModeInterface.js +0 -131
  43. package/lib/build.js +0 -86
  44. package/lib/constants.js +0 -39
  45. package/lib/dev.js +0 -98
  46. package/lib/extensionsService.js +0 -54
  47. package/lib/plugins/codeCheckingPlugin.js +0 -26
  48. package/lib/plugins/codeInjectionPlugin.js +0 -30
  49. package/lib/plugins/devBuildPlugin.js +0 -153
  50. package/lib/plugins/manifestPlugin.js +0 -109
  51. package/lib/server.js +0 -57
  52. package/lib/utils.js +0 -36
package/lib/constants.js DELETED
@@ -1,39 +0,0 @@
1
- const OUTPUT_DIR = 'dist';
2
- const MAIN_APP_CONFIG = 'app.json';
3
- const MANIFEST_FILE = 'manifest.json';
4
-
5
- const VITE_DEFAULT_PORT = 5173;
6
- const WEBSOCKET_PORT = 5174;
7
-
8
- const ROLLUP_OPTIONS = {
9
- // Deps to exclude from the bundle
10
- external: ['react', 'react-dom', '@remote-ui/react'],
11
- output: {
12
- // Maps libs to the variables to be injected via the window
13
- globals: {
14
- react: 'React',
15
- '@remote-ui/react': 'RemoteUI',
16
- },
17
- extend: true,
18
- },
19
- };
20
-
21
- const EXTENSIONS_MESSAGE_VERSION = 1;
22
- const WEBSOCKET_MESSAGE_VERSION = 0;
23
-
24
- const SERVER_CAPABILITIES = [
25
- // Supports running app functions locally
26
- 'app-functions-local-dev',
27
- ];
28
-
29
- module.exports = {
30
- ROLLUP_OPTIONS,
31
- OUTPUT_DIR,
32
- MAIN_APP_CONFIG,
33
- MANIFEST_FILE,
34
- EXTENSIONS_MESSAGE_VERSION,
35
- VITE_DEFAULT_PORT,
36
- WEBSOCKET_MESSAGE_VERSION,
37
- WEBSOCKET_PORT,
38
- SERVER_CAPABILITIES,
39
- };
package/lib/dev.js DELETED
@@ -1,98 +0,0 @@
1
- const { createServer } = require('vite');
2
- const path = require('path');
3
- const startDevServer = require('./server');
4
- const devBuildPlugin = require('./plugins/devBuildPlugin');
5
- const { getUrlSafeFileName } = require('./utils');
6
- const {
7
- VITE_DEFAULT_PORT,
8
- WEBSOCKET_PORT,
9
- OUTPUT_DIR,
10
- } = require('./constants');
11
-
12
- async function _createViteDevServer({
13
- outputDir,
14
- extensionConfig,
15
- webSocketPort,
16
- baseMessage,
17
- root,
18
- cardConfigs,
19
- }) {
20
- return await createServer({
21
- root,
22
- logLevel: 'silent',
23
- appType: 'custom',
24
- mode: 'development',
25
- server: {
26
- middlewareMode: true,
27
- hmr: {
28
- port: webSocketPort,
29
- },
30
- watch: {
31
- ignored: [
32
- path.join(outputDir, '/**/*'),
33
- '**/src/app/app.functions/**/*',
34
- '**/app.json',
35
- '**/package.json',
36
- '**/package-lock.json',
37
- ],
38
- },
39
- },
40
- build: {
41
- rollupOptions: {
42
- input: extensionConfig.data.module.file,
43
- output: getUrlSafeFileName(extensionConfig.data.module.file),
44
- },
45
- },
46
- plugins: [
47
- devBuildPlugin({
48
- extensionConfig,
49
- outputDir,
50
- baseMessage,
51
- cardConfigs,
52
- }),
53
- ],
54
- clearScreen: false,
55
- });
56
- }
57
-
58
- async function startDevMode({
59
- extensionConfig,
60
- functionsConfig,
61
- outputDir = OUTPUT_DIR,
62
- expressPort = VITE_DEFAULT_PORT,
63
- webSocketPort = WEBSOCKET_PORT,
64
- root = process.cwd(),
65
- cardConfigs,
66
- }) {
67
- if (!extensionConfig) {
68
- throw new Error('Unable to determine which extension to run');
69
- }
70
-
71
- const baseMessage = Object.freeze({
72
- appName: extensionConfig.data.appName,
73
- title: extensionConfig.data.title,
74
- callback: `http://hslocal.net:${expressPort}/${extensionConfig.output}`,
75
- });
76
-
77
- const viteDevServer = await _createViteDevServer({
78
- outputDir,
79
- extensionConfig,
80
- webSocketPort,
81
- baseMessage,
82
- root,
83
- cardConfigs,
84
- });
85
-
86
- return startDevServer({
87
- outputDir,
88
- expressPort,
89
- webSocketPort,
90
- baseMessage,
91
- viteDevServer,
92
- functionsConfig,
93
- });
94
- }
95
-
96
- module.exports = {
97
- startDevMode,
98
- };
@@ -1,54 +0,0 @@
1
- const path = require('path');
2
- const { EXTENSIONS_MESSAGE_VERSION } = require('./constants');
3
- const { loadManifest } = require('./utils');
4
-
5
- class ExtensionsService {
6
- constructor() {
7
- this.endpoint = '/extensions';
8
- }
9
-
10
- add(server, webSocketPort, outputDir, baseMessage, capabilities) {
11
- server.get(
12
- this.endpoint,
13
- this.generateExtensionsHandler(
14
- baseMessage,
15
- webSocketPort,
16
- outputDir,
17
- capabilities
18
- )
19
- );
20
- return [this.endpoint];
21
- }
22
-
23
- generateExtensionsHandler(
24
- baseMessage,
25
- webSocketPort,
26
- outputDir,
27
- capabilities = []
28
- ) {
29
- return function extensionsHandler(_req, res) {
30
- try {
31
- const output = path.parse(baseMessage.callback).name;
32
-
33
- const response = {
34
- websocket: `ws://localhost:${webSocketPort}`,
35
- version: EXTENSIONS_MESSAGE_VERSION,
36
- capabilities,
37
- extensions: [
38
- {
39
- ...baseMessage,
40
- manifest: loadManifest(outputDir, output),
41
- },
42
- ],
43
- };
44
- res.status(200).json(response);
45
- } catch (e) {
46
- res.status(500).json({
47
- message: 'Unable to determine which extensions are running',
48
- });
49
- }
50
- };
51
- }
52
- }
53
-
54
- module.exports = new ExtensionsService();
@@ -1,26 +0,0 @@
1
- const fs = require('fs');
2
- const { logger } = require('@hubspot/cli-lib/logger');
3
-
4
- function codeCheckingPlugin(options = {}) {
5
- const { output } = options;
6
- return {
7
- name: 'ui-extensions-code-checking-plugin',
8
- enforce: 'post',
9
- writeBundle(__options, __bundle) {
10
- try {
11
- const code = fs.readFileSync(output).toString();
12
- if (
13
- !code.includes('const extend = (...args) => self.extend(...args);')
14
- ) {
15
- logger.warn(
16
- 'Unable to determine if your extension entry point is calling hubspot.extend, this may prevent it from rendering as expected'
17
- );
18
- }
19
- } catch (e) {
20
- logger.error('Unable to load bundle for code checking');
21
- }
22
- },
23
- };
24
- }
25
-
26
- module.exports = codeCheckingPlugin;
@@ -1,30 +0,0 @@
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;
@@ -1,153 +0,0 @@
1
- const { ROLLUP_OPTIONS, WEBSOCKET_MESSAGE_VERSION } = require('../constants');
2
- const { build } = require('vite');
3
- const manifestPlugin = require('./manifestPlugin');
4
- const { stripAnsiColorCodes } = require('../utils');
5
- const codeCheckingPlugin = require('./codeCheckingPlugin');
6
- const path = require('path');
7
- const { logger } = require('@hubspot/cli-lib/logger');
8
-
9
- function devBuildPlugin(options = {}) {
10
- let lastBuildError;
11
- const { extensionConfig, outputDir, baseMessage, cardConfigs } = options;
12
- const versionedBaseMessage = {
13
- ...baseMessage,
14
- version: WEBSOCKET_MESSAGE_VERSION,
15
- };
16
-
17
- const handleBuildError = (error, server) => {
18
- const { plugin, errors, frame, loc, id } = error;
19
- // Filter out our custom plugins, but send everything else
20
- if (!plugin?.startsWith('ui-extensions')) {
21
- server.ws.send({
22
- ...versionedBaseMessage,
23
- event: 'error',
24
- error: {
25
- details: {
26
- errors,
27
- formattedError: stripAnsiColorCodes(frame),
28
- location: loc,
29
- file: id,
30
- },
31
- },
32
- });
33
- }
34
- };
35
-
36
- const devBuild = async server => {
37
- try {
38
- await build({
39
- logLevel: 'warn',
40
- mode: 'development',
41
- define: {
42
- 'process.env.NODE_ENV': JSON.stringify(
43
- process.env.NODE_ENV || 'development'
44
- ),
45
- },
46
- esbuild: {
47
- tsconfigRaw: {
48
- compilerOptions: {
49
- preserveValueImports: true,
50
- },
51
- },
52
- },
53
- build: {
54
- lib: {
55
- entry: extensionConfig.data.module.file,
56
- name: extensionConfig.output,
57
- formats: ['iife'],
58
- fileName: () => extensionConfig.output,
59
- },
60
- rollupOptions: {
61
- ...ROLLUP_OPTIONS,
62
- plugins: [
63
- manifestPlugin({
64
- minify: false,
65
- output: extensionConfig.output,
66
- }),
67
- codeCheckingPlugin({
68
- output: path.join(outputDir, extensionConfig.output),
69
- }),
70
- ],
71
- output: {
72
- ...ROLLUP_OPTIONS.output,
73
- sourcemap: 'inline',
74
- },
75
- },
76
- outDir: outputDir,
77
- emptyOutDir: true,
78
- minify: false,
79
- },
80
- clearScreen: false,
81
- });
82
- lastBuildError = null;
83
- return true;
84
- } catch (error) {
85
- lastBuildError = error;
86
- logger.debug(error);
87
- handleBuildError(error, server);
88
- return false;
89
- }
90
- };
91
-
92
- let localServer;
93
- return {
94
- name: 'ui-extensions-dev-build-plugin',
95
- enforce: 'pre',
96
- configureServer: async server => {
97
- // Store a reference to the server to be used in hooks that don't get the server injected
98
- // See https://vitejs.dev/guide/api-plugin.html#configureserver for information on this pattern
99
- localServer = server;
100
- localServer.ws.on('connection', () => {
101
- logger.info('Browser connected and listening for bundle updates');
102
- localServer.ws.send({
103
- ...versionedBaseMessage,
104
- event: 'start',
105
- });
106
-
107
- if (lastBuildError) {
108
- handleBuildError(lastBuildError, server);
109
- }
110
- });
111
-
112
- await devBuild(localServer);
113
- },
114
- handleHotUpdate: async ({ file, server }) => {
115
- if (cardConfigs.includes(file)) {
116
- return [];
117
- }
118
- const successful = await devBuild(server);
119
-
120
- if (!successful) {
121
- return [];
122
- }
123
-
124
- logger.info(`Extension ${extensionConfig.data.title} updated, compiled`);
125
-
126
- if (server.ws.clients.size === 0) {
127
- logger.debug('Bundle updated, no browsers connected to notify');
128
- return [];
129
- }
130
-
131
- logger.debug('Bundle updated, notifying connected browsers');
132
- server.ws.send({
133
- ...versionedBaseMessage,
134
- event: 'update',
135
- });
136
- return [];
137
- },
138
- buildEnd(error) {
139
- if (error) {
140
- logger.error(error);
141
- }
142
- logger.debug('Sending shutdown message to connected browsers');
143
- if (localServer && localServer.ws) {
144
- localServer.ws.send({
145
- ...versionedBaseMessage,
146
- event: 'shutdown',
147
- });
148
- }
149
- },
150
- };
151
- }
152
-
153
- module.exports = devBuildPlugin;
@@ -1,109 +0,0 @@
1
- const { readFileSync } = require('fs');
2
- const { normalize } = require('path');
3
- const { MANIFEST_FILE } = require('../constants');
4
- const path = require('path');
5
- const { logger } = require('@hubspot/cli-lib/logger');
6
-
7
- const PACKAGE_LOCK_FILE = 'package-lock.json';
8
- const PACKAGE_FILE = 'package.json';
9
- const EXTENSIONS_PATH = 'src/app/extensions/';
10
-
11
- function manifestPlugin(options = {}) {
12
- return {
13
- name: 'ui-extensions-manifest-generation-plugin',
14
- enforce: 'post', // run after default rollup plugins
15
- generateBundle(_rollupOptions, bundle) {
16
- const { output, minify = false } = options;
17
- try {
18
- const filename = path.parse(output).name;
19
- const manifest = _generateManifestContents(bundle);
20
- this.emitFile({
21
- type: 'asset',
22
- source: minify
23
- ? JSON.stringify(manifest)
24
- : JSON.stringify(manifest, null, 2),
25
- fileName: normalize(`${filename}-${MANIFEST_FILE}`),
26
- });
27
- } catch (e) {
28
- logger.warn(`\nUnable to write manifest file in ${output}, ${e}`);
29
- }
30
- },
31
- };
32
- }
33
-
34
- function _generateManifestContents(bundle) {
35
- const baseManifest = {
36
- package: _loadPackageFile(),
37
- };
38
-
39
- // The keys to bundle are the filename without any path information
40
- const bundles = Object.keys(bundle).filter(cur => cur.endsWith('.js'));
41
- if (bundles.length === 1) {
42
- return {
43
- ..._generateManifestEntry(bundle[bundles[0]]),
44
- ...baseManifest,
45
- };
46
- }
47
-
48
- const manifest = bundles.reduce((acc, current) => {
49
- return {
50
- ...acc,
51
- [current]: _generateManifestEntry(bundle[current], false),
52
- };
53
- }, {});
54
-
55
- return {
56
- ...manifest,
57
- ...baseManifest,
58
- };
59
- }
60
-
61
- function _generateManifestEntry(subBundle) {
62
- const { facadeModuleId, moduleIds, modules } = subBundle;
63
- return {
64
- entry: _stripPathPriorToExtDir(facadeModuleId),
65
- modules: _buildModulesInfo(moduleIds, modules),
66
- };
67
- }
68
-
69
- function _loadJsonFileSafely(filename) {
70
- try {
71
- return JSON.parse(readFileSync(filename).toString());
72
- } catch (e) {
73
- return undefined;
74
- }
75
- }
76
-
77
- function _loadPackageFile() {
78
- // Look for package-lock.json then fallback to package.json
79
- return (
80
- _loadJsonFileSafely(PACKAGE_LOCK_FILE) || _loadJsonFileSafely(PACKAGE_FILE)
81
- );
82
- }
83
-
84
- function _stripPathPriorToExtDir(filepath) {
85
- return filepath?.split(EXTENSIONS_PATH).pop();
86
- }
87
-
88
- function _buildModulesInfo(moduleIds, modules) {
89
- return moduleIds.reduce(
90
- (acc, mod) => {
91
- const { renderedExports } = modules[mod];
92
-
93
- const moduleData = {
94
- module: _stripPathPriorToExtDir(mod),
95
- renderedExports,
96
- };
97
-
98
- if (moduleData.module.includes('node_modules')) {
99
- acc.external.push(moduleData);
100
- } else {
101
- acc.internal.push(moduleData);
102
- }
103
- return acc;
104
- },
105
- { internal: [], external: [] }
106
- );
107
- }
108
-
109
- module.exports = manifestPlugin;
package/lib/server.js DELETED
@@ -1,57 +0,0 @@
1
- const express = require('express');
2
- const cors = require('cors');
3
- const { SERVER_CAPABILITIES } = require('./constants');
4
- const extensionsService = require('./extensionsService');
5
- const {
6
- AppFunctionExecutionService,
7
- } = require('@hubspot/app-functions-dev-server');
8
- const { logger } = require('@hubspot/cli-lib/logger');
9
-
10
- function startDevServer({
11
- outputDir,
12
- expressPort,
13
- webSocketPort,
14
- baseMessage,
15
- viteDevServer,
16
- functionsConfig,
17
- }) {
18
- const app = express();
19
-
20
- // Setup middleware
21
- app.use(cors());
22
- app.use(express.static(outputDir));
23
-
24
- app.use(
25
- '/api/crm-extensibility/execution/internal/v3',
26
- AppFunctionExecutionService({ ...functionsConfig, logger })
27
- );
28
- logger.info('Serving app functions locally');
29
-
30
- const endpointsAdded = extensionsService.add(
31
- app,
32
- webSocketPort,
33
- outputDir,
34
- baseMessage,
35
- SERVER_CAPABILITIES
36
- );
37
-
38
- endpointsAdded.forEach(endpoint => {
39
- logger.debug(`Listening at http://hslocal.net:${expressPort}${endpoint}`);
40
- });
41
-
42
- // Vite middlewares needs to go last because it's greedy and will block other middleware
43
- app.use(viteDevServer.middlewares);
44
-
45
- const server = app.listen({ port: expressPort }, () => {
46
- logger.debug(`Listening at ${baseMessage.callback}`);
47
- });
48
-
49
- return async function shutdown() {
50
- await viteDevServer.pluginContainer.close();
51
- // Stop new connections to express server
52
- server.close(() => {});
53
- logger.info('Extension dev server done cleaning up');
54
- };
55
- }
56
-
57
- module.exports = startDevServer;
package/lib/utils.js DELETED
@@ -1,36 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const { MANIFEST_FILE } = require('./constants');
4
-
5
- function getUrlSafeFileName(filePath) {
6
- const { name } = path.parse(filePath);
7
- return encodeURIComponent(`${name}.js`);
8
- }
9
-
10
- // Strips ANSI color codes out of strings because we don't want to pass them to the browser
11
- function stripAnsiColorCodes(stringWithColorCodes) {
12
- if (!stringWithColorCodes) {
13
- return null;
14
- }
15
- return stringWithColorCodes.replace(
16
- // eslint-disable-next-line no-control-regex
17
- /[\u001b][[]*([0-9]{1,4};?)*[m]/g,
18
- ''
19
- );
20
- }
21
-
22
- function loadManifest(outputDir, output) {
23
- try {
24
- return JSON.parse(
25
- fs.readFileSync(path.join(outputDir, `${output}-${MANIFEST_FILE}`))
26
- );
27
- } catch (e) {
28
- return {};
29
- }
30
- }
31
-
32
- module.exports = {
33
- getUrlSafeFileName,
34
- stripAnsiColorCodes,
35
- loadManifest,
36
- };