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
22
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.0 to 0.0.1-prealpha.1

cli.js

53

build.js

@@ -1,32 +0,43 @@

/* eslint-disable hubspot-dev/no-async-await */
/*global module*/
'use es6';
const { build } = require('vite');
const { ROLLUP_OPTIONS } = require('./constants');
async function buildExtensions(config, outputDir) {
async function buildAllExtensions(config, outputDir) {
const extensionKeys = Object.keys(config);
for (let i = 0; i < extensionKeys.length; ++i) {
const { data } = config[extensionKeys[i]];
await build({
build: {
lib: {
entry: data?.module?.file,
name: data.output,
formats: ['iife'],
fileName: () => data.output,
},
rollupOptions: ROLLUP_OPTIONS,
outDir: outputDir,
emptyOutDir: i === 0,
minify: false,
},
});
await buildSingleExtension(
data?.module.file,
data?.output,
outputDir,
i === 0
);
}
}
async function buildSingleExtension(
file,
outputFileName,
outputDir,
emptyOutDir = true
) {
await build({
build: {
lib: {
entry: file,
name: outputFileName,
formats: ['iife'],
fileName: () => outputFileName,
},
rollupOptions: ROLLUP_OPTIONS,
outDir: outputDir,
emptyOutDir,
minify: false,
},
});
}
module.exports = {
buildExtensions,
buildAllExtensions,
buildSingleExtension,
};

@@ -1,4 +0,1 @@

/* eslint-disable hubspot-dev/no-async-await */
/*global process, module */
'use es6';
const prompts = require('prompts');

@@ -10,7 +7,7 @@ const logger = require('./logger');

async function getExtensionConfig(configuration) {
if (process.env.EXTENSION && configuration[process.env.EXTENSION]) {
const { data } = configuration[process.env.EXTENSION];
async function getExtensionConfig(configuration, extension) {
if (extension && configuration[extension]) {
const { data } = configuration[extension];
return {
key: process.env.EXTENSION,
key: extension,
name: data.title,

@@ -86,6 +83,8 @@ file: data?.module?.file,

const outputConfig = {};
try {
mainAppConfig.extensions.crm.cards.forEach(card => {
const extensionsRemoved = card.file.replace('extensions/', '');
const cardConfig = require(path.join(process.cwd(), extensionsRemoved));
mainAppConfig.extensions.crm.cards.forEach(card => {
const extensionsRemoved = card.file.replace('extensions/', '');
const cardPath = path.join(process.cwd(), extensionsRemoved);
try {
const cardConfig = require(cardPath);
const extensionFileName = cardConfig.data?.module?.file;

@@ -97,8 +96,13 @@ outputConfig[extensionFileName] = cardConfig;

outputConfig[extensionFileName].data.appName = projectConfig.name;
});
} catch (e) {
logger.error(e.message);
process.exit(1);
}
} catch (e) {
let errorMessage = e?.message;
if (e?.code === 'MODULE_NOT_FOUND') {
errorMessage = `Unable to load "${cardPath}" file. \nPlease make sure you are running the command from the src/app/extensions directory and that your card JSON config exists within it.`;
}
logger.error(errorMessage);
process.exit(1);
}
});
return outputConfig;

@@ -105,0 +109,0 @@ }

@@ -1,7 +0,5 @@

/*global module */
'use es6';
const VITE_DEFAULT_PORT = 5173;
const MAIN_APP_CONFIG = 'app.json';
const PROJECT_CONFIG = 'hsproject.json';
const OUTPUT_DIR = 'dist';

@@ -26,2 +24,3 @@ const ROLLUP_OPTIONS = {

PROJECT_CONFIG,
OUTPUT_DIR,
};

@@ -1,4 +0,1 @@

/* eslint-disable hubspot-dev/no-async-await */
/*global module, process*/
'use es6';
const express = require('express');

@@ -15,4 +12,4 @@ const chokidar = require('chokidar');

async function _devBuild(config, outputDir) {
const extensionConfig = await getExtensionConfig(config);
async function _devBuild(config, outputDir, extension) {
const extensionConfig = await getExtensionConfig(config, extension);
build({

@@ -125,4 +122,4 @@ build: {

async function startDevMode(config, outputDir, port) {
const extensionConfig = await _devBuild(config, outputDir);
async function startDevMode(config, outputDir, port, extension) {
const extensionConfig = await _devBuild(config, outputDir, extension);
_startDevServer(outputDir, port, extensionConfig);

@@ -129,0 +126,0 @@ }

@@ -1,4 +0,1 @@

/*global module*/
'use es6';
const { cyan, red, yellow, magenta } = require('console-log-colors');

@@ -5,0 +2,0 @@

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

@@ -12,5 +12,10 @@ "main": "index.js",

},
"files": [
"**/*.js"
],
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.3",
"command-line-args": "^5.2.1",
"command-line-usage": "^7.0.1",
"console-log-colors": "^0.4.0",

@@ -24,5 +29,11 @@ "express": "^4.18.2",

"bin": {
"hs-ui-extensions-dev-server": "run.js"
"hs-ui-extensions-dev-server": "run.js",
"hs-ui-extensions-remote-build": "remoteBuild.js"
},
"gitHead": "eb6b25c4f33ce83c1727cdddb7024267f0b63a70"
"eslintConfig": {
"env": {
"node": true
}
},
"gitHead": "d9459af4e3027da7ae3fa9a9d91c48c1f612677b"
}
# ui-extensions-dev-server
This package contains the build script for bundling and serving remote extensions locally. It checks for a config file named `extension-config.json` in directory the dev server command is ran from. This config file is optional, and the below example contains what the default values are.
Example config file:
## Overview
This package contains the cli for running HubSpot UI extensions locally, as well as running a production build for debugging purposes.
```json
{
"version": "1.0",
"extensions": {
"example-app": {
"file": "App.tsx"
}
}
}
```
### Help
If for any reason you need help with the `ui-extensions-dev-server` you can access the help menu in a few ways:
- `hs-ui-extensions-dev-server`
- `hs-ui-extensions-dev-server -h`
- `hs-ui-extensions-dev-server --help`
### Local Development
Prompts you which of your extensions you would like to run in local dev mode. Once you have made your selection, it sets up a watcher on the entry point file for that project as well as any child files. Any changes to those files will trigger a local build which will bundle your code and output the bundle in a `dist/` directory inside of your current working directory. To initiate local dev mode in the browser, you just need to navigate to the CRM where your the deployed version of your card lives. The browser will detect that you have the dev server running, and make the connection on your behalf.
| Action | Command |
| - | - |
| Standard Local Dev | `hs-ui-extensions-dev-server dev` |
| Bypass Prompt | `hs-ui-extensions-dev-server dev --extension MyEntryPointFile.jsx` <br /> `hs-ui-extensions-dev-server dev -e MyEntryPointFile.jsx`|
### Production Build
This should not be required in the standard development workflow. It is there to aid debugging the output of an extension. This command will run a build similar to what we will run when these extensions are uploaded. The output will be written into a `dist/` directory in the current working directory.
| Action | Command |
| - | - |
| Build All Extensions | `hs-ui-extensions-dev-server build` |
| Build a single extension | `hs-ui-extensions-dev-server build --extension MyEntryPointFile.jsx` <br /> `hs-ui-extensions-dev-server build -e MyEntryPointFile.jsx`|
#!/usr/bin/env node
/* eslint-disable hubspot-dev/no-async-await */
/*global process*/
'use es6';
const { startDevMode } = require('./dev');
const { loadConfig } = require('./config');
const { buildExtensions } = require('./build');
const { VITE_DEFAULT_PORT } = require('./constants');
const { buildAllExtensions, buildSingleExtension } = require('./build');
const { VITE_DEFAULT_PORT, OUTPUT_DIR } = require('./constants');
const { parseArgs, showHelp } = require('./cli');
const { getUrlSafeFileName } = require('./utils');
const DEV_MODE = process.env.DEV_MODE || false;
const OUTPUT_DIR = 'dist';
const PORT = process.env.PORT || VITE_DEFAULT_PORT;
const { DEV_MODE, BUILD_MODE, port, extension, help } = parseArgs();
const PORT = port || VITE_DEFAULT_PORT;
const config = loadConfig();
if (help || !(DEV_MODE || BUILD_MODE)) {
showHelp(OUTPUT_DIR);
}
if (DEV_MODE) {
startDevMode(config, OUTPUT_DIR, PORT);
} else {
buildExtensions(config, OUTPUT_DIR);
startDevMode(loadConfig(), OUTPUT_DIR, PORT, extension);
} else if (BUILD_MODE) {
if (extension) {
buildSingleExtension(extension, getUrlSafeFileName(extension), OUTPUT_DIR);
} else {
buildAllExtensions(loadConfig(), OUTPUT_DIR);
}
}
#!/usr/bin/env node
/*global process */
'use es6';

@@ -5,0 +3,0 @@ const { testBuild } = require('./testBuild');

@@ -1,4 +0,1 @@

/*global module, process */
'use es6';
const { execSync } = require('child_process');

@@ -9,5 +6,4 @@ const fs = require('fs');

function testBuild(logger) {
logger.info('Build Tests started 🀞');
execSync('hs-ui-extensions-dev-server');
function _testHelper(command, outputDirFiles) {
execSync(command);

@@ -17,6 +13,3 @@ // Make sure the files are getting generated in the dist dir

const filesInOutputDir = fs.readdirSync(distDir);
assert.deepStrictEqual(filesInOutputDir, [
'PhoneLines.js',
'ProgressBarApp.js',
]);
assert.deepStrictEqual(filesInOutputDir, outputDirFiles);

@@ -40,8 +33,53 @@ // Spot check the file contents to make sure they seem ok

});
}
logger.info('Build Tests passed πŸš€');
function testDefaultBuildPath(logger) {
logger.warn('- Test default build path started 🀞');
_testHelper('hs-ui-extensions-dev-server build', [
'PhoneLines.js',
'ProgressBarApp.js',
]);
logger.info('- Test default build path passed πŸš€');
}
function testBuildWithExtensionFlag(logger) {
logger.warn('- Test build with flags started 🀞');
_testHelper(
'hs-ui-extensions-dev-server build --extension ProgressBarApp.tsx',
['ProgressBarApp.js']
);
logger.info('- Test build with flags passed πŸš€');
}
function testDefInfraBuildFileName(logger) {
logger.warn('- Test build with entrypoint as arg 🀞');
_testHelper('hs-ui-extensions-remote-build ProgressBarApp.tsx', [
'ProgressBarApp.js',
]);
logger.info('- Test build with entrypoint as arg πŸš€');
}
function testDevInfraBuildFileNameWithPathPrefix(logger) {
logger.warn('- Test build with config file as arg 🀞');
_testHelper(
'hs-ui-extensions-remote-build some/super/long/file/path/to/the/extensions/PhoneLines.tsx',
['PhoneLines.js']
);
logger.info('- Test build with config file as arg πŸš€');
}
function testBuild(logger) {
logger.warn('\nBuild Tests started - External Devs 🀞');
testDefaultBuildPath(logger);
testBuildWithExtensionFlag(logger);
logger.info('Build Tests passed - External DevsπŸš€');
logger.warn('\nBuild Tests started - Dev Infra 🀞');
testDefInfraBuildFileName(logger);
testDevInfraBuildFileNameWithPathPrefix(logger);
logger.info('Build Tests passed - Dev Infra πŸš€');
}
module.exports = {
testBuild,
};

@@ -1,4 +0,1 @@

/*global module, process */
'use es6';
const { spawn } = require('child_process');

@@ -17,4 +14,6 @@ const fs = require('fs');

const port = 5172;
function testDevServer(logger, devServerProcess) {
logger.info('Dev Server Tests started 🀞');
logger.warn('\nDev Server Tests started 🀞');

@@ -24,5 +23,9 @@ // We need to use spawn here because it will put the process into the background,

// the express server and websocket server are starting properly
devServerProcess = spawn('hs-ui-extensions-dev-server', {
env: { ...process.env, DEV_MODE: 'true', EXTENSION: 'PhoneLines.tsx' },
});
devServerProcess = spawn('hs-ui-extensions-dev-server', [
'dev',
'--extension',
'PhoneLines.tsx',
'--port',
`${port}`,
]);

@@ -32,7 +35,9 @@ devServerProcess.stdout.on('data', buffer => {

if (data.includes('built in')) {
testBuild(testResults);
testBuild(testResults, logger);
}
if (data.includes('listening') && data.includes('localhost:5173')) {
testExpressServer(testResults);
testWebSocketServer(testResults);
if (data.includes('listening') && data.includes(`localhost:${port}`)) {
setTimeout(() => {
testExpressServer(testResults, logger);
testWebSocketServer(testResults, logger);
}, 1000);
}

@@ -80,3 +85,3 @@ });

// check the contents of the files.
function testBuild(results) {
function testBuild(results, logger) {
// // Make sure the files are getting generated in the dist dir

@@ -102,2 +107,3 @@ const distDir = path.join(process.cwd(), 'dist');

});
logger.info('- Build succeeded πŸš€');
results.buildTestPassed = true;

@@ -108,7 +114,7 @@ }

// and that it is serving the files as expected.
function testExpressServer(results) {
function testExpressServer(results, logger) {
http.get(
{
host: 'localhost',
port: 5173,
port,
path: '/PhoneLines.js',

@@ -120,2 +126,3 @@ },

}
logger.info('- Express server connected and serving files πŸš€');
results.expressTestPassed = true;

@@ -128,3 +135,3 @@ }

// that we are able to recieve messages from it.
function testWebSocketServer(results) {
function testWebSocketServer(results, logger) {
const fileContents = fs

@@ -134,3 +141,3 @@ .readFileSync(path.join('dist', 'PhoneLines.js'))

const ws = new WebSocket('ws://localhost:5173');
const ws = new WebSocket(`ws://localhost:${port}`);
ws.on('message', messageBuffer => {

@@ -142,2 +149,3 @@ const message = JSON.parse(messageBuffer.toString());

assert(message.callback, `data:text/javascript;base64,${fileContents}`);
logger.info('- WebSocket server connected and sending messages πŸš€');
results.webSocketTestPassed = true;

@@ -144,0 +152,0 @@ });

@@ -1,4 +0,1 @@

/*global module*/
'use es6';
function getUrlSafeFileName(filePath) {

@@ -5,0 +2,0 @@ const fileName = filePath.split('/').pop();

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