@hubspot/ui-extensions-dev-server
Advanced tools
Comparing version 0.0.1-prealpha.0 to 0.0.1-prealpha.1
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, | ||
}; |
11
dev.js
@@ -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`| |
28
run.js
#!/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(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
26317
16
751
28
6
9
5
+ Addedcommand-line-args@^5.2.1
+ Addedcommand-line-usage@^7.0.1
+ Addedansi-styles@4.3.0(transitive)
+ Addedarray-back@3.1.06.2.2(transitive)
+ Addedchalk@4.1.2(transitive)
+ Addedchalk-template@0.4.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcommand-line-args@5.2.1(transitive)
+ Addedcommand-line-usage@7.0.3(transitive)
+ Addedfind-replace@3.0.0(transitive)
+ Addedhas-flag@4.0.0(transitive)
+ Addedlodash.camelcase@4.3.0(transitive)
+ Addedsupports-color@7.2.0(transitive)
+ Addedtable-layout@4.1.1(transitive)
+ Addedtypical@4.0.07.3.0(transitive)
+ Addedwordwrapjs@5.1.0(transitive)