Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More β†’
Socket
Sign inDemoInstall
Socket

@hubspot/ui-extensions-dev-server

Package Overview
Dependencies
Maintainers
23
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hubspot/ui-extensions-dev-server - npm Package Compare versions

Comparing version 0.0.1-prealpha.8 to 0.1.0

server.js

21

config.js
const prompts = require('prompts');
const logger = require('./logger');
const path = require('path');
const { MAIN_APP_CONFIG, PROJECT_CONFIG } = require('./constants');
const { MAIN_APP_CONFIG } = require('./constants');
const { getUrlSafeFileName } = require('./utils');
async function getExtensionConfig(configuration, extension) {
if (extension && configuration[extension]) {
const { data } = configuration[extension];
const extensionOptions = Object.keys(configuration);
if (
(extension && configuration[extension]) ||
extensionOptions.length === 1
) {
const extensionToRun = extension || extensionOptions[0];
const { data } = configuration[extensionToRun];
return {
key: extension,
key: extensionToRun,
name: data.title,

@@ -19,3 +25,2 @@ file: data?.module?.file,

const extensionOptions = Object.keys(configuration);
const response = await prompts(

@@ -69,6 +74,2 @@ [

const projectConfig = _loadRequiredConfigFile(
path.join(process.cwd(), `../../../${PROJECT_CONFIG}`)
);
const mainAppConfig = _loadRequiredConfigFile(configPath);

@@ -112,3 +113,3 @@

);
outputConfig[entryPointPath].data.appName = projectConfig.name;
outputConfig[entryPointPath].data.appName = mainAppConfig.name;
} catch (e) {

@@ -115,0 +116,0 @@ let errorMessage = e?.message;

@@ -19,2 +19,5 @@ const VITE_DEFAULT_PORT = 5173;

const EXTENSIONS_MESSAGE_VERSION = 0;
const WEBSOCKET_MESSAGE_VERSION = 0;
module.exports = {

@@ -26,2 +29,4 @@ VITE_DEFAULT_PORT,

OUTPUT_DIR,
EXTENSIONS_MESSAGE_VERSION,
WEBSOCKET_MESSAGE_VERSION,
};

@@ -1,15 +0,9 @@

const express = require('express');
const chokidar = require('chokidar');
const { WebSocketServer } = require('ws');
const http = require('http');
const logger = require('./logger');
const { build } = require('vite');
const { getExtensionConfig } = require('./config');
const fs = require('fs');
const path = require('path');
const { ROLLUP_OPTIONS } = require('./constants');
const startDevServer = require('./server');
const manifestPlugin = require('./plugins/manifestPlugin');
async function _devBuild(config, outputDir, extension) {
const extensionConfig = await getExtensionConfig(config, extension);
build({
async function _buildDevBundle(outputDir, extensionConfig) {
await build({
define: {

@@ -36,3 +30,9 @@ 'process.env.NODE_ENV': JSON.stringify(

},
rollupOptions: ROLLUP_OPTIONS,
rollupOptions: {
...ROLLUP_OPTIONS,
plugins: [
...(ROLLUP_OPTIONS.plugins || []),
manifestPlugin({ minify: false, extensionConfig }),
],
},
outDir: outputDir,

@@ -43,89 +43,8 @@ emptyOutDir: true,

});
return extensionConfig;
}
function _startDevServer(outputDir, port, extensionConfig) {
const app = express();
const server = http.createServer(app);
// Host the OUTPUT_DIR
app.use(express.static(outputDir));
// Setup websocket server to send messages to browser on bundle update
const wss = new WebSocketServer({ server });
const callback = `http://localhost:${port}/${extensionConfig?.output}`;
function broadcast(message) {
if (wss.clients.size === 0) {
logger.warn('No browsers connected to update');
return;
}
wss.clients.forEach(client => {
client.send(JSON.stringify(message));
});
}
wss.on('connection', client => {
let base64Callback;
try {
base64Callback = fs
.readFileSync(
path.join(process.cwd(), outputDir, extensionConfig?.output)
)
.toString('base64');
} catch (e) {
logger.warn(
'File not found:',
path.join(process.cwd(), outputDir, extensionConfig?.output)
);
}
logger.info('Browser connected and listening for bundle updates');
client.send(
JSON.stringify({
event: 'start',
appName: extensionConfig?.appName,
extension: extensionConfig?.name,
callback: base64Callback
? `data:text/javascript;base64,${base64Callback}`
: undefined,
})
);
});
// Start the express and websocket servers
server.listen({ port }, () => {
logger.warn(`Listening at ${callback}`);
});
// Setup a watcher on the dist directory and update broadcast
//to all clients when an event is observed
chokidar.watch(outputDir).on('change', file => {
const base64Callback = fs
.readFileSync(path.join(process.cwd(), file))
.toString('base64');
logger.debug(`${file} updated, reloading extension`);
broadcast({
event: 'update',
appName: extensionConfig?.appName,
extension: extensionConfig?.name,
callback: `data:text/javascript;base64,${base64Callback}`,
});
});
process.on('SIGINT', () => {
logger.warn('\nSending shutdown signal to connected browser');
broadcast({
event: 'shutdown',
appName: extensionConfig?.appName,
extension: extensionConfig?.name,
});
process.exit(0);
});
}
async function startDevMode(config, outputDir, port, extension) {
const extensionConfig = await _devBuild(config, outputDir, extension);
_startDevServer(outputDir, port, extensionConfig);
const extensionConfig = await getExtensionConfig(config, extension);
await _buildDevBundle(outputDir, extensionConfig);
startDevServer(outputDir, port, extensionConfig);
}

@@ -132,0 +51,0 @@

{
"name": "@hubspot/ui-extensions-dev-server",
"version": "0.0.1-prealpha.8",
"version": "0.1.0",
"description": "",

@@ -26,3 +26,4 @@ "main": "index.js",

"plugins/manifestPlugin.js",
"index.js"
"index.js",
"server.js"
],

@@ -35,2 +36,3 @@ "license": "MIT",

"console-log-colors": "^0.4.0",
"cors": "^2.8.5",
"express": "^4.18.2",

@@ -40,3 +42,3 @@ "process": "^0.11.10",

"vite": "^4.0.4",
"ws": "^8.12.1"
"ws": "^8.13.0"
},

@@ -62,3 +64,3 @@ "bin": {

},
"gitHead": "b5968f431fe29ee9977e6aacdd9b9ab3aa08af0c"
"gitHead": "abac5960e6277af827714dcd4150ad7606e6a481"
}

@@ -15,5 +15,9 @@ const { readFileSync } = require('fs');

generateBundle(_rollupOptions, bundle) {
const { output = DEFAULT_MANIFEST_NAME, minify = false } = options;
const {
output = DEFAULT_MANIFEST_NAME,
minify = false,
extensionConfig,
} = options;
try {
const manifest = _generateManifestContents(bundle);
const manifest = _generateManifestContents(bundle, extensionConfig);
this.emitFile({

@@ -33,5 +37,6 @@ type: 'asset',

function _generateManifestContents(bundle) {
function _generateManifestContents(bundle, extension) {
const baseManifest = {
package: _loadPackageFile(),
extension,
};

@@ -38,0 +43,0 @@

@@ -1,3 +0,5 @@

# ui-extensions-dev-server
# UI Extensions – Dev Server
Development server to run and test your HubSpot UI Extensions.
## Overview

@@ -4,0 +6,0 @@ This package contains the cli for running HubSpot UI extensions locally, as well as running a production build for debugging purposes.

@@ -19,3 +19,4 @@ #!/usr/bin/env node

process.on('uncaughtException', () => {
process.on('uncaughtException', e => {
console.error(e);
handleFailure();

@@ -22,0 +23,0 @@ });

@@ -7,3 +7,5 @@ const { execSync } = require('child_process');

function _testHelper(command, outputDirFiles) {
execSync(command);
if (command) {
execSync(command);
}

@@ -71,7 +73,7 @@ // Make sure the files are getting generated in the dist dir

function testDefInfraBuildFileName(logger) {
logger.warn('- Test build with entrypoint as arg 🀞');
_testHelper('hs-ui-extensions-remote-build ProgressBarApp.tsx', [
'ProgressBarApp.js',
'manifest.json',
]);
const { remoteBuild } = require('../index');
logger.warn('- Test remoteBuild function 🀞');
remoteBuild(process.cwd(), 'ProgressBarApp.tsx', 'dist');
_testHelper(null, ['ProgressBarApp.js', 'manifest.json']);
logger.info('- Test build with entrypoint as arg πŸš€');

@@ -78,0 +80,0 @@ }

@@ -7,6 +7,11 @@ const { spawn } = require('child_process');

const WebSocket = require('ws');
const {
WEBSOCKET_MESSAGE_VERSION,
EXTENSIONS_MESSAGE_VERSION,
} = require('../constants');
const testResults = {
buildTestPassed: false,
expressTestPassed: false,
expressStaticTestPassed: false,
extensionsEndpointPassed: false,
webSocketTestPassed: false,

@@ -16,2 +21,3 @@ };

const port = 5172;
const host = 'hslocal.net';

@@ -37,3 +43,6 @@ function testDevServer(logger, devServerProcess) {

}
if (data.includes('listening') && data.includes(`localhost:${port}`)) {
if (
data.includes('listening') &&
data.includes(`${host}:${port}/extensions`)
) {
setTimeout(() => {

@@ -77,6 +86,12 @@ testExpressServer(testResults, logger);

buildTestPassed,
expressTestPassed,
expressStaticTestPassed,
extensionsEndpointPassed,
webSocketTestPassed,
} = testResults;
return buildTestPassed && expressTestPassed && webSocketTestPassed;
return (
buildTestPassed &&
expressStaticTestPassed &&
extensionsEndpointPassed &&
webSocketTestPassed
);
}

@@ -90,3 +105,3 @@

const filesInOutputDir = fs.readdirSync(distDir);
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js']);
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js', 'manifest.json']);
const fileContents = fs

@@ -117,3 +132,3 @@ .readFileSync(path.join(distDir, filesInOutputDir[0]))

{
host: 'localhost',
host,
port,

@@ -127,5 +142,36 @@ path: '/PhoneLines.js',

logger.info('- Express server connected and serving files πŸš€');
results.expressTestPassed = true;
results.expressStaticTestPassed = true;
}
);
http.get(
{
host,
port,
path: '/extensions',
},
response => {
let body = '';
response.on('data', chunk => {
body += chunk.toString();
});
response.on('end', () => {
assert(response.statusCode === 200);
body = JSON.parse(body);
assert(body.extensions);
assert.equal(body.extensions.length, 1);
const extension = body.extensions.pop();
assert(extension.manifest);
assert.equal(extension.appName, 'Example App React UI');
assert.equal(extension.title, 'Phone Lines');
assert.equal(
extension.callback,
`http://${host}:${port}/PhoneLines.js`
);
assert.equal(body.websocket, `ws://localhost:${port}`);
assert.strictEquals(body.version, EXTENSIONS_MESSAGE_VERSION);
logger.info('- Express serving extension data πŸš€');
results.extensionsEndpointPassed = true;
});
}
);
}

@@ -136,15 +182,12 @@

function testWebSocketServer(results, logger) {
const fileContents = fs
.readFileSync(path.join('dist', 'PhoneLines.js'))
.toString('base64');
const ws = new WebSocket(`ws://localhost:${port}`);
ws.on('message', messageBuffer => {
const message = JSON.parse(messageBuffer.toString());
assert(message.event === 'start' || message.event === 'update');
assert(message.appName === 'example-app');
assert.strictEqual(message.extension, 'Phone Lines');
assert(message.callback, `data:text/javascript;base64,${fileContents}`);
assert(message.event === 'start');
assert.strictEqual(message.appName, 'Example App React UI');
assert.strictEqual(message.title, 'Phone Lines');
assert.strictEqual(message.version, WEBSOCKET_MESSAGE_VERSION);
logger.info('- WebSocket server connected and sending messages πŸš€');
results.webSocketTestPassed = true;
ws.close(); // The test passed, close the connection
});

@@ -151,0 +194,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚑️ by Socket Inc