diff --git a/src/BaseCommand.js b/src/BaseCommand.js index 63bfcae..47042b8 100644 --- a/src/BaseCommand.js +++ b/src/BaseCommand.js @@ -27,7 +27,11 @@ class BaseCommand extends Command { async catch (error) { const { flags } = await this.parse(this.prototype) if (flags.verbose) { - aioLogger.error(error) + if (error.diagnostics) { + aioLogger.error(JSON.stringify(error.diagnostics, null, 2)) + } else { + aioLogger.error(error) + } } this.handleError(error, flags.verbose) } diff --git a/src/commands/app/dev/index.js b/src/commands/app/dev/index.js index 5b15684..d71b631 100644 --- a/src/commands/app/dev/index.js +++ b/src/commands/app/dev/index.js @@ -102,9 +102,14 @@ class Dev extends BaseCommand { * @param {object} config the config for the app */ async verifyActionConfig (config) { - const actionConfig = config.manifest.full.packages + const actionConfig = config.manifest?.full?.packages const errors = [] + // there might not be a runtime manifest, that's ok + if (!actionConfig) { + return + } + // 1. all actions in sequences must exist Object.entries(actionConfig).forEach(([_, pkg]) => { // iterate through each package const sequences = pkg?.sequences || {} @@ -140,7 +145,9 @@ class Dev extends BaseCommand { async runAppBuild (extensionConfig) { this.log('Building the app...') - await buildActions(extensionConfig, null /* filterActions[] */, false /* skipCheck */) + if (extensionConfig.app.hasBackend) { + await buildActions(extensionConfig, null /* filterActions[] */, false /* skipCheck */) + } } async runOneExtensionPoint (name, config, flags) { @@ -204,12 +211,11 @@ class Dev extends BaseCommand { } if (hasBackend) { this.displayActionUrls(actionUrls) + const { watcherCleanup } = await createWatcher({ config, isLocal: true, inprocHook }) + cleanup.add(() => watcherCleanup(), 'cleaning up action watcher...') + cleanup.wait() } this.log('press CTRL+C to terminate the dev environment') - - const { watcherCleanup } = await createWatcher({ config, isLocal: true, inprocHook }) - cleanup.add(() => watcherCleanup(), 'cleaning up action watcher...') - cleanup.wait() } async getOrGenerateCertificates ({ pubCertPath, privateKeyPath, devKeysDir, devKeysConfigKey, maxWaitTimeSeconds = 20 }) { diff --git a/src/lib/run-dev.js b/src/lib/run-dev.js index 972ed50..d90e39f 100644 --- a/src/lib/run-dev.js +++ b/src/lib/run-dev.js @@ -66,9 +66,9 @@ async function runDev (runOptions, config, _inprocHookRunner) { const distFolder = devConfig.actions.dist const serveLogger = coreLogger('serve', { level: process.env.LOG_LEVEL, provider: 'winston' }) - serveLogger.debug('config.manifest is', JSON.stringify(devConfig.manifest.full.packages, null, 2)) + serveLogger.debug('config.manifest is', JSON.stringify(devConfig.manifest?.full?.packages, null, 2)) - const actionConfig = devConfig.manifest.full.packages + const actionConfig = devConfig.manifest?.full?.packages const hasFrontend = devConfig.app.hasFrontend const hasBackend = devConfig.app.hasBackend const httpsSettings = runOptions?.parcel?.https @@ -117,55 +117,51 @@ async function runDev (runOptions, config, _inprocHookRunner) { if (hasFrontend) { const liveReloadServer = livereload.createServer({ https: serverOptions }) liveReloadServer.watch(devConfig.web.distDev) + serveLogger.info(`Watching web folder ${devConfig.web.distDev}...`) liveReloadServer.server.once('connection', () => { setTimeout(() => { liveReloadServer.refresh('/') }, 100) }) - try { - utils.writeConfig(devConfig.web.injectedConfig, actionUrls) + utils.writeConfig(devConfig.web.injectedConfig, actionUrls) - const bundlerPortToUse = parseInt(process.env.BUNDLER_PORT) || BUNDLER_DEFAULT_PORT - const bundlerPort = await getPort({ port: bundlerPortToUse }) + const bundlerPortToUse = parseInt(process.env.BUNDLER_PORT) || BUNDLER_DEFAULT_PORT + const bundlerPort = await getPort({ port: bundlerPortToUse }) - if (bundlerPort !== bundlerPortToUse) { - serveLogger.info(`Could not use bundler port ${bundlerPortToUse}, using port ${bundlerPort} instead`) - } + if (bundlerPort !== bundlerPortToUse) { + serveLogger.info(`Could not use bundler port ${bundlerPortToUse}, using port ${bundlerPort} instead`) + } - const entries = devConfig.web.src + '/**/*.html' - bundleOptions.serveOptions = { - port: bundlerPort, - https: httpsSettings - } + const entries = devConfig.web.src + '/**/*.html' + bundleOptions.serveOptions = { + port: bundlerPort, + https: httpsSettings + } - const bundler = await bundle(entries, devConfig.web.distDev, bundleOptions, serveLogger.debug.bind(serveLogger)) - await bundler.run() // run it once + const bundler = await bundle(entries, devConfig.web.distDev, bundleOptions, serveLogger.debug.bind(serveLogger)) + await bundler.run() // run it once - subscription = await bundler.watch((err, event) => { - if (err) { - // fatal error - throw err - } + subscription = await bundler.watch((err, event) => { + if (err) { + // fatal error + throw err + } - serveLogger.info(`${event.changedAssets.size} static asset(s) changed`) - const limit = runOptions.verbose ? Infinity : CHANGED_ASSETS_PRINT_LIMIT - if (event.changedAssets.size <= limit) { - event.changedAssets.forEach((value, key, map) => { - serveLogger.info('\t-->', value) - }) - } - if (event.type === 'buildSuccess') { - const bundles = event.bundleGraph.getBundles() - serveLogger.info(`✨ Built ${bundles.length} bundles in ${event.buildTime}ms!`) - } else if (event.type === 'buildFailure') { - serveLogger.error(event.diagnostics) - } - }) - } catch (err) { - console.error(err) - serveLogger.error(err.diagnostics) - } + serveLogger.info(`${event.changedAssets.size} static asset(s) changed`) + const limit = runOptions.verbose ? Infinity : CHANGED_ASSETS_PRINT_LIMIT + if (event.changedAssets.size <= limit) { + event.changedAssets.forEach((value, key, map) => { + serveLogger.info('\t-->', value) + }) + } + if (event.type === 'buildSuccess') { + const bundles = event.bundleGraph.getBundles() + serveLogger.info(`✨ Built ${bundles.length} bundles in ${event.buildTime}ms!`) + } else if (event.type === 'buildFailure') { + serveLogger.error(event.diagnostics) + } + }) } const app = express() diff --git a/test/BaseCommand.test.js b/test/BaseCommand.test.js index 9f1b876..026b71f 100644 --- a/test/BaseCommand.test.js +++ b/test/BaseCommand.test.js @@ -182,6 +182,15 @@ describe('catch', () => { expect(command.error).toHaveBeenCalledWith(expect.stringContaining(errorList.join('\n'))) }) + test('will handle errors with diagnostics property when using --verbose flag', async () => { + command.argv = ['--verbose'] + const errorWithDiagnostics = new Error('fake error') + errorWithDiagnostics.diagnostics = { some: 'property ' } + await command.catch(errorWithDiagnostics) + + expect(command.error).toHaveBeenCalledWith(expect.stringContaining('fake error')) + }) + test('will handle errors without stack traces when using --verbose flag', async () => { command.argv = ['--verbose'] const errorWithoutStack = new Error('fake error') diff --git a/test/commands/app/dev/index.test.js b/test/commands/app/dev/index.test.js index d202526..cef1746 100644 --- a/test/commands/app/dev/index.test.js +++ b/test/commands/app/dev/index.test.js @@ -169,7 +169,6 @@ describe('run', () => { } command.getAppExtConfigs.mockResolvedValueOnce({ myextension: appConfig }) - const serverCleanup = () => console.log('server cleanup') mockRunDev.mockResolvedValue({ frontEndUrl: 'https://localhost:9080', actionUrls: { @@ -235,7 +234,6 @@ describe('run', () => { test('run, no flags, has frontend, no backend', async () => { command.argv = [] const appConfig = { - manifest: { full: { packages: {} } }, hooks: { }, app: { diff --git a/test/lib/run-dev.test.js b/test/lib/run-dev.test.js index 550b71a..aeefc9e 100644 --- a/test/lib/run-dev.test.js +++ b/test/lib/run-dev.test.js @@ -1449,23 +1449,22 @@ describe('runDev', () => { } }) - test('has front end, has back end, bundler watch error', async () => { - const actionPath = fixturePath('actions/successNoReturnAction.js') - const config = createConfig({ - hasFrontend: true, - hasBackend: true, - packageName: 'mypackage', - actions: { - myaction: { - function: actionPath + describe('has front end, has back end, bundler errors', () => { + test('bundler watch error', async () => { + const actionPath = fixturePath('actions/successNoReturnAction.js') + const config = createConfig({ + hasFrontend: true, + hasBackend: true, + packageName: 'mypackage', + actions: { + myaction: { + function: actionPath + } } - } - }) - const runOptions = createRunOptions({ cert: 'my-cert', key: 'my-key' }) - const hookRunner = () => {} + }) + const runOptions = createRunOptions({ cert: 'my-cert', key: 'my-key' }) + const hookRunner = () => {} - // 1. error in bundle.watch - { const bundleEvent = createBundlerEvent() const bundleErr = { diagnostics: 'something went wrong' } mockLibWeb.bundle.mockResolvedValue({ @@ -1475,16 +1474,24 @@ describe('runDev', () => { } }) - const { frontendUrl, actionUrls, serverCleanup } = await runDev(runOptions, config, hookRunner) - await serverCleanup() + await expect(runDev(runOptions, config, hookRunner)).rejects.toEqual(bundleErr) + }) - expect(frontendUrl).toBeDefined() - expect(Object.keys(actionUrls).length).toBeGreaterThan(0) - expect(mockLogger.error).toHaveBeenCalledWith(bundleErr.diagnostics) - } + test('bundler build error', async () => { + const actionPath = fixturePath('actions/successNoReturnAction.js') + const config = createConfig({ + hasFrontend: true, + hasBackend: true, + packageName: 'mypackage', + actions: { + myaction: { + function: actionPath + } + } + }) + const runOptions = createRunOptions({ cert: 'my-cert', key: 'my-key' }) + const hookRunner = () => {} - // 2. error in bundle build - { const bundlerEventParams = { type: 'buildFailure', diagnostics: 'something went wrong' } const bundleEvent = createBundlerEvent(bundlerEventParams) mockLibWeb.bundle.mockResolvedValue({ @@ -1500,10 +1507,23 @@ describe('runDev', () => { expect(frontendUrl).toBeDefined() expect(Object.keys(actionUrls).length).toBeGreaterThan(0) expect(mockLogger.error).toHaveBeenCalledWith(bundlerEventParams.diagnostics) - } + }) + + test('unknown build event type', async () => { + const actionPath = fixturePath('actions/successNoReturnAction.js') + const config = createConfig({ + hasFrontend: true, + hasBackend: true, + packageName: 'mypackage', + actions: { + myaction: { + function: actionPath + } + } + }) + const runOptions = createRunOptions({ cert: 'my-cert', key: 'my-key' }) + const hookRunner = () => {} - // 2. unknown buildEvent type - { const bundlerEventParams = { type: 'unknown_event_type', diagnostics: 'something went wrong 2' } const bundleEvent = createBundlerEvent(bundlerEventParams) mockLibWeb.bundle.mockResolvedValue({ @@ -1518,7 +1538,7 @@ describe('runDev', () => { expect(frontendUrl).toBeDefined() expect(Object.keys(actionUrls).length).toBeGreaterThan(0) - } + }) }) })